package mods.ocminecart.common.entityextend; import com.google.common.base.Charsets; import li.cil.oc.api.API; import li.cil.oc.api.network.Packet; import li.cil.oc.api.network.WirelessEndpoint; import mods.ocminecart.OCMinecart; import mods.ocminecart.common.util.ItemUtil; import mods.ocminecart.common.util.StringUtil; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityMinecart; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.world.World; import net.minecraftforge.common.IExtendedEntityProperties; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; public abstract class RemoteCartExtender implements WirelessEndpoint, IExtendedEntityProperties{ public final static String PROP_ID = OCMinecart.MODID+".RCcart"; protected EntityMinecart entity; private World worldObj; private int posX; private int posY; private int posZ; private boolean stashInit = false; private boolean valid = false; protected boolean enabled=false; protected boolean respbroadcast = true; private int nextResp = -1; private String nextAddr = null; protected int respport = 1; protected int cmdport = 2; private String address; private String owner = null; private byte[] password = null; private boolean lock = false; private ItemStack drop = null; private int maxWlanStrength=4; private int curWlanStrength=4; @Override public int x() { return posX; } @Override public int y() { return posY; } @Override public int z() { return posZ; } public final void setEnabled(boolean state) { this.setEnabled(state,false); } public final void setEnabled(boolean state, boolean force) { if(!valid) return; if(this.enabled != state || force){ this.enabled = state; if(state){ if(this.maxWlanStrength>0)API.network.joinWirelessNetwork(this); RemoteExtenderRegister.addRemoteUpdate(this); } else{ API.network.leaveWirelessNetwork(this); RemoteExtenderRegister.removeRemoteUpdate(this); } this.changeEnabled(); } } public final boolean isEnabled() { return enabled; } protected void changeEnabled() { NBTTagCompound nbt = this.entity.getEntityData(); nbt.removeTag(OCMinecart.MODID+":rc_settings"); if(isEnabled()){ //reset some settings curWlanStrength = 4; address = UUID.randomUUID().toString(); respport = 1; cmdport = 2; respbroadcast = true; nextResp = -1; lock=false; } } protected void processCommand(String cmd, Object[] args) { if(cmd == null || cmd.equals("doc")){ String a1 = (args.length >= 1 && (args[0] instanceof String)) ? (String)args[0] : null; if(a1 != null && this.getCommands().contains(a1)) { String doc = this.getDoc(a1); doc = (doc == null || doc == "") ? "No documentation available" : doc; this.sendPacket(new Object[]{doc}, this.getRespPort(), this.getRespAddress()); } else { this.sendPacket(new Object[]{this.getCmdList()}, this.getRespPort(), this.getRespAddress()); } } else if(cmd.equals("response_port")){ boolean isValid = args.length>=1 && (args[0] instanceof Double); int port = (isValid) ? (int)(double)(Double)args[0] : 0; //Double is a Class and have to be converted to a double (type) port = Math.max(-1, port); if(isValid) this.respport = port; this.sendPacket(new Object[]{this.respport}, this.getRespPort(), this.getRespAddress()); } else if(cmd.equals("command_port")){ boolean isValid = args.length>=1 && (args[0] instanceof Double); int port = (isValid) ? (int)(double)(Double)args[0] : 0; port = Math.max(-1, port); if(isValid) this.cmdport = port; this.sendPacket(new Object[]{this.cmdport}, this.getRespPort(), this.getRespAddress()); } else if(cmd.equals("response_broadcast")){ boolean isValid = args.length>=1 && (args[0] instanceof Boolean); boolean value = (isValid) ? (Boolean)args[0] : false; if(isValid) this.respbroadcast = value; this.sendPacket(new Object[]{this.respbroadcast}, this.getRespPort(), this.getRespAddress()); } else if(cmd.equals("wlan_strength")){ int strength = (args.length>0 && (args[0] instanceof Double)) ? (int)((double)((Double)args[0])) : this.curWlanStrength; //Double (object) -> double (number) -> int strength = Math.min(strength, this.maxWlanStrength); this.curWlanStrength = strength; this.sendPacket(new Object[]{strength, this.maxWlanStrength}, this.getRespPort(), this.getRespAddress()); } } private String getCmdList(){ Iterator<String> it = this.getCommands().iterator(); String st = "{"; while(it.hasNext()) { st += it.next()+ ((it.hasNext()) ? "," : ""); } st += "}"; return st; } protected final int getRespPort() { if(this.respport>=0) return this.respport; return this.nextResp; } protected final String getRespAddress() { if(this.respbroadcast) return null; return this.nextAddr; } protected final void sendPacket(Object[] msg, int port, String des){ if(this.getRespPort()<0) return; Packet packet = API.network.newPacket(address, des, port, msg); API.network.sendWirelessPacket(this, this.curWlanStrength, packet); } protected List<String> getCommands(){ ArrayList<String> cmd = new ArrayList<String>(); cmd.add("doc"); cmd.add("response_port"); cmd.add("command_port"); cmd.add("response_broadcast"); cmd.add("wlan_strength"); return cmd; } protected String getDoc(String cmd){ if(cmd == null) return null; if(cmd.equals("doc")) return "doc([func:string]):table or string -- get a list of functions or a documentation for a function."; else if(cmd.equals("response_port")) return "response_port([port:number]):number -- sets the response port and returns the new port. -1 to response on the same port as the last message"; else if(cmd.equals("command_port")) return "command_port([port:number]):number -- sets the command port and returns the new port. -1 to accept all ports"; else if(cmd.equals("response_broadcast")) return "response_broadcast([value:boolean]):boolean -- if the value is true it will respond with private messages."; else if(cmd.equals("wlan_strength")) return "wlan_strength([value:number]):number,number -- get/set the current and get the max. wireless strength."; return null; } public void update() { if(this.world().isRemote) return; boolean hasEntity = worldObj.loadedEntityList.contains(this.entity); boolean chunkLoaded = worldObj.getChunkFromBlockCoords((int)entity.posX, (int)entity.posZ).isChunkLoaded; if(this.entity.isDead || this.entity.getDamage()>=this.getMaxModuleDamage()){ this.setEnabled(false, true); if(this.entity.getDamage()>=this.getMaxModuleDamage()){ this.dropItem(); } } else{ if(!chunkLoaded || !hasEntity){ /* * Stop the updates on chunk unload. * A chunk reload should reinitialize the entity and reactivate the updates. */ RemoteExtenderRegister.removeRemoteUpdate(this); } else if(stashInit){ stashInit = false; if(this.maxWlanStrength>0) API.network.joinWirelessNetwork(this); } this.posX=(int) entity.posX; this.posY=(int) entity.posY; this.posZ=(int) entity.posZ; if(this.maxWlanStrength>0) API.network.updateWirelessNetwork(this); } } @Override public World world() { return worldObj; } @Override public void receivePacket(Packet packet, WirelessEndpoint sender){ if(packet.ttl()<0 || !inRange(sender,curWlanStrength)) return; if(!(packet.destination()==null || packet.destination().equals(this.address)) || !(this.cmdport==-1 || packet.port()==this.cmdport)) return; if(!(packet.data()[0] instanceof byte[]) || ((packet.data().length<2 || !(packet.data()[1] instanceof byte[])) && this.hasPassword())) return; boolean usePassword = false; if(this.hasPassword()){ String passw = new String((byte[])packet.data()[1],Charsets.UTF_8); if(passw.length()<=2 || !passw.substring(0, 2).equals("::") || !this.isCorrectPassword(passw.substring(2, passw.length()))) return; usePassword=true; } else if(packet.data().length>2 && packet.data()[1] instanceof byte[]){ String passw = new String((byte[])packet.data()[1],Charsets.UTF_8); if(passw.length()>=2 || passw.substring(0, 2).equals("::")) usePassword=true; } this.nextAddr = packet.source(); this.nextResp = packet.port(); String cmd = new String((byte[])packet.data()[0],Charsets.UTF_8); Object[] data = (this.getCommands().contains(cmd))? this.processPacket(packet.data(), usePassword) : new Object[]{}; this.processCommand((this.getCommands().contains(cmd))? cmd : null, data); } private Object[] processPacket(Object[] data, boolean hasPassword){ int dataoffset = (hasPassword) ? 2 : 1; if(data.length-dataoffset<1) return new Object[]{}; Object[] res = new Object[data.length-dataoffset]; for(int i=dataoffset;i<data.length;i+=1) { if(data[i] instanceof byte[]) { res[i-dataoffset] = new String((byte[]) data[i], Charsets.UTF_8); } else { res[i-dataoffset] = data[i]; } } return res; } @Override public void saveNBTData(NBTTagCompound nbt) { nbt.setBoolean(OCMinecart.MODID+":rc_enabled", enabled); if(enabled){ NBTTagCompound rc = new NBTTagCompound(); if(this.address!=null)rc.setString("rc_address", this.address); rc.setBoolean("rc_respbroadcast", this.respbroadcast); rc.setInteger("rc_respport", this.respport); rc.setInteger("rc_cmdport", this.cmdport); if(password!=null) rc.setByteArray("rc_password", this.password); rc.setInteger("rc_maxwlan", this.maxWlanStrength); rc.setInteger("rc_curwlan", this.curWlanStrength); if(owner!=null) rc.setString("rc_owner", this.owner); else rc.removeTag("rc_owner"); rc.setBoolean("rc_locked", this.lock); if(drop!=null){ NBTTagCompound dropnbt = new NBTTagCompound(); drop.writeToNBT(dropnbt); rc.setTag("rc_dropitem", dropnbt); } this.writeModuleNBT(rc); nbt.setTag(OCMinecart.MODID+":rc_settings", rc); } } @Override public void loadNBTData(NBTTagCompound nbt) { //boolean hasEntity = worldObj.loadedEntityList.contains(this.entity); //if(!hasEntity && this.worldObj != entity.worldObj) return; if(nbt.hasKey(OCMinecart.MODID+":rc_enabled")) enabled = nbt.getBoolean(OCMinecart.MODID+":rc_enabled"); else enabled = false; if(nbt.hasKey(OCMinecart.MODID+":rc_settings") && this.enabled){ NBTTagCompound rc = nbt.getCompoundTag(OCMinecart.MODID+":rc_settings"); if(rc.hasKey("rc_address")) this.address = rc.getString("rc_address"); else if(rc.hasKey("rc_uuid")) this.address = rc.getString("rc_uuid"); if(rc.hasKey("rc_respbroadcast")) this.respbroadcast = rc.getBoolean("rc_respbroadcast"); if(rc.hasKey("rc_respport")) this.respport = rc.getInteger("rc_respport"); if(rc.hasKey("rc_cmdport")) this.cmdport = rc.getInteger("rc_cmdport"); if(rc.hasKey("rc_password")) this.password = rc.getByteArray("rc_password"); if(rc.hasKey("rc_maxwlan")) this.maxWlanStrength = rc.getInteger("rc_maxwlan"); if(rc.hasKey("rc_curwlan")) this.curWlanStrength = rc.getInteger("rc_curwlan"); if(rc.hasKey("rc_dropitem")){ NBTTagCompound dropnbt = rc.getCompoundTag("rc_dropitem"); drop=ItemStack.loadItemStackFromNBT(dropnbt); } if(rc.hasKey("rc_owner")) this.owner=rc.getString("rc_owner"); if(rc.hasKey("rc_locked")) this.lock=rc.getBoolean("rc_locked"); this.loadModuleNBT(rc); } if(this.enabled){ stashInit = true; boolean c = RemoteExtenderRegister.addRemoteUpdate(this); if(!c){ RemoteExtenderRegister.removeRemoteUpdate(this.entity); //if it failed remove existing Remotes from the entity RemoteExtenderRegister.addRemoteUpdate(this); } } else{ RemoteExtenderRegister.removeRemoteUpdate(this); // Just to make sure nothing bad happens } } @Override public void init(Entity entity, World world) { if(!(entity instanceof EntityMinecart)) return; //if(RemoteExtenderRegister.containsEntity(entity.getUniqueID())) return; this.entity = (EntityMinecart)entity; this.worldObj = world; this.valid=true; } public boolean inRange(WirelessEndpoint w, double range){ int x = this.x() - w.x(); int y = this.y() - w.y(); int z = this.z() - w.z(); return (x*x) + (y*y) + (z*z) <= (range * range); } public String getAddress(){ return (this.enabled) ? this.address : null; } protected void loadModuleNBT(NBTTagCompound nbt){}; protected void writeModuleNBT(NBTTagCompound nbt){} public void onAnalyzeModule(EntityPlayer p) { p.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE+"Address: "+EnumChatFormatting.RESET+this.address)); p.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE+"Response Port: "+EnumChatFormatting.RESET+this.respport)); p.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE+"Command Port: "+EnumChatFormatting.RESET+this.cmdport)); p.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE+"Boradcast Response: "+EnumChatFormatting.RESET+this.respbroadcast)); p.addChatComponentMessage(new ChatComponentText(EnumChatFormatting.LIGHT_PURPLE+"Wireless Strength: "+EnumChatFormatting.RESET+this.curWlanStrength+" / "+this.maxWlanStrength)); } public void dropItem(){ if(this.drop!=null) ItemUtil.dropItem(this.drop, this.entity.worldObj, this.entity.posX, this.entity.posY, this.entity.posZ, true); } public boolean editableByPlayer(EntityPlayer p, boolean noPublic){ return (!this.lock && !noPublic) || this.owner==null || p.getUniqueID().toString().equals(this.owner) || MinecraftServer.getServer().getConfigurationManager().func_152596_g(p.getGameProfile()); } public EntityMinecart getCart(){ return this.entity; } //Max damage the cart/locomotive can have. public int getMaxModuleDamage(){ return 40; } public ItemStack getRemoteItem(){ return this.drop; } public void setRemoteItem(ItemStack drop){ if(drop!=null){ this.drop=drop.copy(); this.drop.stackSize = 1; } else this.drop=null; } public int getMaxWlanStrength() { return maxWlanStrength; } public void setMaxWlanStrength(int maxWlanStrength) { if(this.maxWlanStrength>=1 && maxWlanStrength<1 && this.enabled) API.network.leaveWirelessNetwork(this); else if(this.maxWlanStrength<1 && maxWlanStrength>=1 && this.enabled) API.network.joinWirelessNetwork(this); this.maxWlanStrength = maxWlanStrength; this.curWlanStrength=Math.min(this.curWlanStrength, this.maxWlanStrength); } public void setPassword(String password){ if(password==null || password.length()<1){ this.password=null; return; } this.password=StringUtil.getMD5Array(password); } public boolean hasPassword(){ return this.password!=null; } public boolean isCorrectPassword(String password){ if(this.password==null) return true; byte[] pass = StringUtil.getMD5Array(password); String hex1 = StringUtil.byteToHex(this.password); String hex2 = StringUtil.byteToHex(pass); return hex1.equals(hex2); } public int getCurWlanStrength() { return curWlanStrength; } public void setCurWlanStrength(int curWlanStrength) { this.curWlanStrength = Math.min(curWlanStrength, maxWlanStrength); } public void setOwner(String uuid){ this.owner=uuid; } public String getOwner(){ return this.owner; } public void setLocked(boolean lock){ this.lock=lock; } public boolean isLocked(){ return this.lock; } }