/**
* Copyright (c) Lambda Innovation, 2013-2016
* This file is part of the AcademyCraft mod.
* https://github.com/LambdaInnovation/AcademyCraft
* Licensed under GPLv3, see project root for more information.
*/
package cn.academy.energy.internal;
import cn.academy.core.AcademyCraft;
import cn.academy.energy.api.block.IWirelessMatrix;
import cn.academy.energy.api.block.IWirelessNode;
import cn.academy.energy.internal.VBlocks.VNNode;
import cn.academy.energy.internal.VBlocks.VWMatrix;
import cn.academy.energy.internal.VBlocks.VWNode;
import cn.lambdalib.util.generic.MathUtils;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import java.util.*;
/**
* @author WeAthFolD
*/
public class WirelessNet {
private static final int UPDATE_INTERVAL = 40;
private static final double BUFFER_MAX = 2000;
private final WiWorldData data;
World world;
private List<VWNode> nodes = new LinkedList<>();
private List<VWNode> toRemoveNodes = new ArrayList<>();
private VWMatrix matrix;
private String ssid;
private String password;
private double buffer;
private int aliveUpdateCounter = UPDATE_INTERVAL;
private boolean disposed = false;
WirelessNet(WiWorldData data, VWMatrix matrix, String ssid, String pass) {
this.data = data;
this.matrix = matrix;
this.ssid = ssid;
this.password = pass;
}
WirelessNet(WiWorldData data, NBTTagCompound tag) {
this.data = data;
//Load the matrix
matrix = new VWMatrix(tag.getCompoundTag("matrix"));
//Load the info
ssid = tag.getString("ssid");
password = tag.getString("password");
buffer = tag.getDouble("buffer");
//Load the node list
NBTTagList list = (NBTTagList) tag.getTag("list");
for(int i = 0; i < list.tagCount(); ++i) {
doAddNode(new VWNode(list.getCompoundTagAt(i)));
}
debug("Loading " + ssid + " from NBT, " + list.tagCount() + " nodes.");
}
NBTTagCompound toNBT() {
NBTTagCompound tag = new NBTTagCompound();
tag.setTag("matrix", matrix.toNBT());
tag.setString("ssid", ssid);
tag.setString("password", password);
tag.setDouble("buffer", buffer);
NBTTagList list = new NBTTagList();
for(VWNode vn : nodes) {
if(!vn.isLoaded(world) || vn.get(world) != null) {
list.appendTag(vn.toNBT());
}
}
tag.setTag("list", list);
debug(ssid + " toNBT()");
return tag;
}
public String getSSID() {
return ssid;
}
public String getPassword() {
return password;
}
public void setSSID(String ssid) {
this.ssid = ssid;
}
public boolean resetPassword(String np) {
password = np;
return true;
}
public boolean isDisposed() {
return disposed;
}
public int getLoad() {
return nodes.size();
}
public int getCapacity() {
World world = data.world;
IWirelessMatrix imat = matrix.get(world);
return imat == null ? 0 : imat.getCapacity();
}
public IWirelessMatrix getMatrix() {
return matrix.get(world);
}
/**
* Dispose (a.k.a. destroy) this network and unlink all its linked nodes.
*/
void dispose() {
disposed = true;
}
boolean addNode(VWNode node, String password) {
if(!password.equals(this.password))
return false;
if(getLoad() >= getCapacity())
return false;
IWirelessMatrix imat = matrix.get(world);
if(imat == null) {
return false;
}
double r = imat.getRange();
if(node.distSq(matrix) > r * r)
return false;
WiWorldData data = getWorldData();
//Check if this node is previously added
WirelessNet other = data.getNetwork(node.get(world));
if(other != null) {
other.removeNode(node);
}
doAddNode(node);
return true;
}
boolean validate() {
if (matrix.isLoaded(world)) {
IWirelessMatrix mat = matrix.get(world);
if (mat == null) {
disposed = true;
}
}
return !disposed;
}
boolean isInRange(int x, int y, int z) {
IWirelessMatrix imat = matrix.get(world);
if(imat == null) {
return false;
}
double r = imat.getRange();
return MathUtils.distanceSq(x, y, z, matrix.x, matrix.y, matrix.z) <= r * r;
}
private void doAddNode(VWNode node) {
//Really add
WiWorldData data = getWorldData();
nodes.add(node);
data.netLookup.put(node, this);
}
void removeNode(VWNode node) {
debug("Removing " + node + " from " + ssid);
toRemoveNodes.add(node);
}
void onCreate(WiWorldData data) {
data.netLookup.put(matrix, this);
}
void onCleanup(WiWorldData data) {
data.netLookup.remove(ssid);
data.netLookup.remove(matrix);
for(VWNode n : nodes) {
data.netLookup.remove(n);
}
}
private WiWorldData getWorldData() {
return data;
}
void tick() {
validate();
if (matrix.isLoaded(world)) {
// Check whether the matrix is valid. The matrix is ALWAYS loaded.
IWirelessMatrix imat = matrix.get(world);
if(imat == null) {
debug("WirelessNet with SSID " + ssid + " matrix destoryed, removing");
dispose();
} else {
// Balance.
// Shuffle in order to not balance one node all the time
// Maybe a bit of slow?
Collections.shuffle(nodes);
double sum = 0, maxSum = 0;
for (VWNode vn : nodes) {
if (vn.isLoaded(world)) {
IWirelessNode node = vn.get(world);
if (node == null) {
removeNode(vn);
} else {
sum += node.getEnergy();
maxSum += node.getMaxEnergy();
}
}
}
// Remove nodes
data.netLookup.keySet().removeAll(toRemoveNodes);
nodes.removeAll(toRemoveNodes);
toRemoveNodes.clear();
double percent = sum / maxSum;
double transferLeft = imat.getBandwidth();
// Loop through and calc
for(VWNode vn : nodes) {
if(vn.isLoaded(world)) {
IWirelessNode node = vn.get(world);
double cur = node.getEnergy();
double targ = node.getMaxEnergy() * percent;
double delta = targ - cur;
delta = Math.signum(delta) * Math.min(Math.abs(delta), Math.min(transferLeft, node.getBandwidth()));
if(buffer + delta > BUFFER_MAX) {
delta = BUFFER_MAX - buffer;
} else if(buffer + delta < 0) {
delta = -buffer;
}
transferLeft -= Math.abs(delta);
buffer += delta;
node.setEnergy(cur + delta);
if(transferLeft == 0)
break;
}
}
}
}
}
private void debug(Object msg) {
if(AcademyCraft.DEBUG_MODE)
AcademyCraft.log.info("WN:" + msg);
}
}