3.PacketHandler.java: ———————————— package com.kingrunes.somnia.common; import com.kingrunes.somnia.Somnia; import com.kingrunes.somnia.api.capability.CapabilityFatigue; import com.kingrunes.somnia.api.capability.IFatigue; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.Unpooled; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.PacketBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientCustomPacketEvent; import net.minecraftforge.fml.common.network.FMLNetworkEvent.ServerCustomPacketEvent; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import javax.annotation.Nullable; import java.io.DataInputStream; import java.io.IOException; public class PacketHandler { /* * Handling */ @SubscribeEvent public void onServerPacket(ServerCustomPacketEvent event) { if (event.getPacket().channel().equals(Somnia.MOD_ID)) onPacket(event.getPacket(), ((NetHandlerPlayServer)event.getHandler()).player); } @SubscribeEvent public void onClientPacket(ClientCustomPacketEvent event) { if (event.getPacket().channel().equals(Somnia.MOD_ID)) onPacket(event.getPacket(), null); } public void onPacket(FMLProxyPacket packet, EntityPlayerMP player) { DataInputStream in = new DataInputStream(new ByteBufInputStream(packet.payload())); try { byte id = in.readByte(); switch (id) { case 0x00: handleGUIOpenPacket(); break; case 0x01: handleWakePacket(player, in); break; case 0x02: handlePropUpdatePacket(in, player); break; case 0x03: handleRightClickBlockPacket(player, in); break; case 0x04: handleRideEntityPacket(player, in); break; } } catch (IOException e) { Somnia.logger.error("Packet handling error", e); } } // CLIENT private void handleGUIOpenPacket() { Minecraft.getMinecraft().addScheduledTask(() -> Somnia.proxy.handleGUIOpenPacket() ); } private void handlePropUpdatePacket(DataInputStream in, @Nullable EntityPlayerMP player) throws IOException { if (player == null || player.world.isRemote) { Minecraft.getMinecraft().addScheduledTask(() -> { try { Somnia.proxy.handlePropUpdatePacket(in); } catch (IOException e) { Somnia.logger.error("Prop update error", e); } }); return; } byte target = in.readByte(); IFatigue props = player.getCapability(CapabilityFatigue.FATIGUE_CAPABILITY, null); if (target == 0x01 && props != null) { int b = in.readInt(); for (int a=0; a<b; a++) { int val = in.readByte(); if (val == 0x00) { props.setFatigue(in.readDouble()); } else if (val == 0x01) { props.setResetSpawn(in.readBoolean()); } else if (val == 0x02) { props.setSleepNormally(in.readBoolean()); } } } } private void handleWakePacket(EntityPlayerMP player, DataInputStream in) { Minecraft.getMinecraft().addScheduledTask(() -> Somnia.proxy.handleWakePacket(player) ); } private void handleRightClickBlockPacket(EntityPlayerMP player, DataInputStream in) throws IOException { BlockPos pos = new BlockPos(in.readInt(), in.readInt(), in.readInt()); EnumFacing side = EnumFacing.values()[in.readByte()]; float hitX = in.readFloat(); float hitY = in.readFloat(); float hitZ = in.readFloat(); player.world.getBlockState(pos).getBlock().onBlockActivated( player.world, pos, player.world.getBlockState(pos), player, EnumHand.MAIN_HAND, side, hitX, hitY, hitZ ); } private void handleRideEntityPacket(EntityPlayerMP player, DataInputStream in) throws IOException { int entityId = in.readInt(); Entity entity = player.world.getEntityByID(entityId); if (entity != null) { player.startRiding(entity); } } /* * Building */ private static PacketBuffer unpooled() { return new PacketBuffer(Unpooled.buffer()); } public static FMLProxyPacket buildGUIOpenPacket() { PacketBuffer buffer = unpooled(); buffer.writeByte(0x00); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildWakePacket() { PacketBuffer buffer = unpooled(); buffer.writeByte(0x01); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildPropUpdatePacket(int target, int prop, boolean value) { PacketBuffer buffer = unpooled(); buffer.writeByte(0x02); buffer.writeByte(target); buffer.writeInt(1); // Number of properties buffer.writeByte(prop); buffer.writeBoolean(value); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildPropUpdatePacket(int target, int prop, double value) { PacketBuffer buffer = unpooled(); buffer.writeByte(0x02); buffer.writeByte(target); buffer.writeInt(1); buffer.writeByte(prop); buffer.writeDouble(value); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildPropUpdatePacket(int target, int prop, double value, int prop2, String value2) { PacketBuffer buffer = unpooled(); buffer.writeByte(0x02); buffer.writeByte(target); buffer.writeInt(2); // Number of properties buffer.writeByte(prop); buffer.writeDouble(value); buffer.writeByte(prop2); buffer.writeString(value2); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildRightClickBlockPacket(BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) { PacketBuffer buffer = unpooled(); buffer.writeByte(0x03); buffer.writeInt(pos.getX()); buffer.writeInt(pos.getY()); buffer.writeInt(pos.getZ()); buffer.writeByte(side.ordinal()); buffer.writeFloat(hitX); buffer.writeFloat(hitY); buffer.writeFloat(hitZ); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } public static FMLProxyPacket buildRideEntityPacket(Entity entity) { PacketBuffer buffer = unpooled(); buffer.writeByte(0x04); buffer.writeInt(entity.getEntityId()); return new FMLProxyPacket(buffer, Somnia.MOD_ID); } } |
2.ServerTickHandler.java ———————————————————— package com.kingrunes.somnia.server; import com.kingrunes.somnia.Somnia; import com.kingrunes.somnia.common.PacketHandler; import com.kingrunes.somnia.common.SomniaConfig; import com.kingrunes.somnia.common.util.SomniaState; import com.kingrunes.somnia.common.util.SomniaUtil; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.play.server.SPacketTimeUpdate; import net.minecraft.server.MinecraftServer; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.WorldServer; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.gameevent.TickEvent; import net.minecraftforge.fml.common.gameevent.TickEvent.Phase; import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; import javax.annotation.Nullable; import java.util.Iterator; import static com.kingrunes.somnia.common.util.SomniaState.*; public class ServerTickHandler { public static final String TRANSLATION_FORMAT = "somnia.status.%s"; private static int activeTickHandlers = 0; public WorldServer worldServer; public SomniaState currentState; public long lastSleepStart, currentSleepPeriod; // Incremented while mbCheck is true, reset when state is changed public long checkTimer = 0, // Used to schedule GUI update packets and sleep state checks lastTpsMillis = 0, liTps = 0, // Counts ticks tps = 0; // Set per second to liTPS, used to work out actual multiplier to send to clients private double multiplier = SomniaConfig.LOGIC.baseMultiplier; private double worldTimeMultiplier = SomniaConfig.PERFORMANCE.fasterWorldTimeMultiplier; public ServerTickHandler(WorldServer worldServer) { this.worldServer = worldServer; } public void tickStart() { if (++checkTimer == 10) { checkTimer = 0; SomniaState prevState = currentState; currentState = SomniaState.getState(this); if (prevState != currentState) { currentSleepPeriod = 0; if (currentState == ACTIVE) // acceleration started { lastSleepStart = worldServer.getWorldTime(); activeTickHandlers++; } else if (prevState == ACTIVE) // acceleration stopped { activeTickHandlers--; if (currentState == EXPIRED || currentState == NOT_NOW) closeGuiWithMessage(currentState.toString()); } } if (currentState == ACTIVE || currentState == WAITING_PLAYERS || currentState == COOLDOWN) { FMLProxyPacket packet = PacketHandler.buildPropUpdatePacket( 0x00, 0x00, currentState == ACTIVE ? (double)tps/20d : .0d, 0x01, currentState == ACTIVE ? SomniaUtil.timeStringForWorldTime(worldServer.getWorldTime()) : "f:"+currentState.toString() ); Somnia.eventChannel.sendToDimension(packet, worldServer.provider.getDimension()); } } if (currentState == ACTIVE) doMultipliedTicking(); } private void closeGuiWithMessage(@Nullable String key) { FMLProxyPacket packet = PacketHandler.buildWakePacket(); Iterator<EntityPlayer> iter = worldServer.playerEntities.iterator(); EntityPlayer ep; while (iter.hasNext()) { ep = iter.next(); if (ep.isPlayerSleeping()) { Somnia.eventChannel.sendTo(packet, (EntityPlayerMP) ep); if (ep.isPlayerSleeping()) // this if might stop random teleporting when players have already woken { ep.wakeUpPlayer(false, true, true); // Stop clients ignoring GUI close packets (major hax) } if (key != null) ep.sendMessage(new TextComponentTranslation(String.format(TRANSLATION_FORMAT, key.toLowerCase()))); } } } private void incrementCounters() { liTps++; currentSleepPeriod++; } private double overflow = .0d; private double worldTimeOverflow = .0d; private void doMultipliedTicking() { /* * We can't run 0.5 of a tick, * so we floor the multiplier and store the difference as overflow to be ran on the next tick */ double target = multiplier + overflow; int liTarget = (int) Math.floor(target); overflow = target - liTarget; long delta = System.currentTimeMillis(); for (int i=0; i<liTarget; i++) doMultipliedServerTicking(); delta = System.currentTimeMillis() - delta; MinecraftServer server = worldServer.getMinecraftServer(); if (server == null) return; server.getPlayerList().sendPacketToAllPlayersInDimension(new SPacketTimeUpdate(worldServer.getTotalWorldTime(), worldServer.getWorldTime(), worldServer.getGameRules().getBoolean("doDaylightCycle")), worldServer.provider.getDimension()); if (delta > (SomniaConfig.LOGIC.delta/activeTickHandlers)) multiplier -= .1d; else multiplier += .1d; if (multiplier > SomniaConfig.LOGIC.multiplierCap) multiplier = SomniaConfig.LOGIC.multiplierCap; if (multiplier < SomniaConfig.LOGIC.baseMultiplier) multiplier = SomniaConfig.LOGIC.baseMultiplier; long currentTimeMillis = System.currentTimeMillis(); if (currentTimeMillis-lastTpsMillis > 1000) { tps = liTps; liTps = 0; lastTpsMillis = currentTimeMillis; } } private void doMultipliedServerTicking() { FMLCommonHandler.instance().onPreWorldTick(worldServer); worldServer.tick(); worldServer.updateEntities(); worldServer.getEntityTracker().tick(); FMLCommonHandler.instance().onPostWorldTick(worldServer); /* * Work around for making sure fatigue is updated with every tick (including Somnia ticks) */ for (Object obj : worldServer.playerEntities) Somnia.forgeEventHandler.onPlayerTick(new TickEvent.PlayerTickEvent(Phase.START, (EntityPlayer) obj)); if (SomniaConfig.PERFORMANCE.fasterWorldTime) { double target = worldTimeMultiplier + worldTimeOverflow; int liTarget = (int) Math.floor(target); worldTimeOverflow = target - liTarget; worldServer.setWorldTime(worldServer.getWorldTime() + liTarget); } incrementCounters(); } } |