/** * Copyright (c) Lambda Innovation, 2013-2015 * 本作品版权由Lambda Innovation所有。 * http://www.li-dev.cn/ * <p/> * This project is open-source, and it is distributed under * the terms of GNU General Public License. You can modify * and distribute freely as long as you follow the license. * 本项目是一个开源项目,且遵循GNU通用公共授权协议。 * 在遵照该协议的情况下,您可以自由传播和修改。 * http://www.gnu.org/licenses/gpl.html */ package cn.academy.energy.internal; import cn.academy.energy.api.block.IWirelessMatrix; import cn.academy.energy.api.block.IWirelessNode; import cn.academy.energy.internal.VBlocks.VWMatrix; import cn.academy.energy.internal.VBlocks.VWNode; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.world.World; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * @author WeAthFolD */ public class WirelessNet { static final int UPDATE_INTERVAL = 40; static final double BUFFER_MAX = 2000; final WiWorldData data; World world; List<VWNode> nodes = new LinkedList(); VWMatrix matrix; String ssid; String password; double buffer; int aliveUpdateCounter = UPDATE_INTERVAL; boolean alive = false; boolean disposed = false; public WirelessNet(WiWorldData data, VWMatrix matrix, String ssid, String pass) { this.data = data; this.matrix = matrix; this.ssid = ssid; this.password = pass; System.out.println("Directly creating " + ssid); } public 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 boolean resetPassword(String p, String np) { if (!p.equals(password)) return false; password = np; return true; } /** * Get whether this matrix is alive (That is, there are >=1 node loaded and should be ticked normally). */ public boolean isAlive() { return alive; } 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(); } /** * 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 isInRange(int x, int y, int z) { IWirelessMatrix imat = matrix.get(world); if (imat == null) { return false; } double r = imat.getRange(); double vec1[] = new double[]{x, y, z}; double vec2[] = new double[]{matrix.x, matrix.y, matrix.z}; if (vec1.length != vec2.length) { throw new RuntimeException("Inconsistent length"); } double ret = 0.0; for (int i = 0; i < vec1.length; ++i) { double d = vec2[i] - vec1[i]; ret += d * d; } return ret <= r * r; } private void doAddNode(VWNode node) { //Really add WiWorldData data = getWorldData(); nodes.add(node); data.netLookup.put(node, this); } void removeNode(VWNode node) { nodes.remove(node); WiWorldData data = getWorldData(); data.netLookup.remove(node); } void onCreate(WiWorldData data) { data.netLookup.put(ssid, this); 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; } /** * This is a slightly costy function. You should buffer the result and query through isAlive(). * query it infrequently. * @return same as isAlive */ private boolean checkIsAlive() { //IF: alive node count > 1 for (VWNode node : nodes) { if (node.isLoaded(world) && node.get(world) != null) { alive = true; return true; } } alive = false; return false; } void tick() { // Filter the not-alive nets and update the state lazily if (!isAlive()) { --aliveUpdateCounter; if (aliveUpdateCounter == 0) { aliveUpdateCounter = UPDATE_INTERVAL; checkIsAlive(); } return; } // 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(); return; } // 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; Iterator<VWNode> iter = nodes.iterator(); while (iter.hasNext()) { VWNode vn = iter.next(); if (vn.isLoaded(world)) { IWirelessNode node = vn.get(world); if (node == null) { debug("Removing " + vn + " from " + ssid); iter.remove(); } else { sum += node.getEnergy(); maxSum += node.getMaxEnergy(); } } } 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) { //AcademyCraft.log.info("WN:" + msg); } }