diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java index d730c06..59c09e2 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapScreen.java @@ -53,7 +53,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI; public class WarsmashGdxMapScreen implements InputProcessor, Screen { public static final boolean ENABLE_AUDIO = true; - private static final boolean ENABLE_MUSIC = true; + private static final boolean ENABLE_MUSIC = false; private final War3MapViewer viewer; private final Rectangle tempRect = new Rectangle(); diff --git a/core/src/com/etheller/warsmash/networking/ClientToServerListener.java b/core/src/com/etheller/warsmash/networking/ClientToServerListener.java index 77b6fa2..972606e 100644 --- a/core/src/com/etheller/warsmash/networking/ClientToServerListener.java +++ b/core/src/com/etheller/warsmash/networking/ClientToServerListener.java @@ -21,4 +21,6 @@ public interface ClientToServerListener { void finishedTurn(SocketAddress sourceAddress, int gameTurnTick); + void framesSkipped(int nFramesSkipped); + } diff --git a/core/src/com/etheller/warsmash/networking/ClientToServerProtocol.java b/core/src/com/etheller/warsmash/networking/ClientToServerProtocol.java index b1da380..50faac5 100644 --- a/core/src/com/etheller/warsmash/networking/ClientToServerProtocol.java +++ b/core/src/com/etheller/warsmash/networking/ClientToServerProtocol.java @@ -9,4 +9,5 @@ public class ClientToServerProtocol { public static final int UNIT_CANCEL_TRAINING = 5; public static final int FINISHED_TURN = 6; public static final int JOIN_GAME = 7; + public static final int FRAMES_SKIPPED = 8; } diff --git a/core/src/com/etheller/warsmash/networking/GameTurnManager.java b/core/src/com/etheller/warsmash/networking/GameTurnManager.java index 6ea66d9..3fc5f1e 100644 --- a/core/src/com/etheller/warsmash/networking/GameTurnManager.java +++ b/core/src/com/etheller/warsmash/networking/GameTurnManager.java @@ -5,6 +5,8 @@ public interface GameTurnManager { void turnCompleted(int gameTurnTick); + void framesSkipped(float skippedCount); + GameTurnManager PAUSED = new GameTurnManager() { @Override public int getLatestCompletedTurn() { @@ -15,6 +17,10 @@ public interface GameTurnManager { public void turnCompleted(final int gameTurnTick) { System.err.println("got turnCompleted(" + gameTurnTick + ") while paused !!"); } + + @Override + public void framesSkipped(final float skippedCount) { + } }; GameTurnManager LOCAL = new GameTurnManager() { @@ -26,5 +32,10 @@ public interface GameTurnManager { @Override public void turnCompleted(final int gameTurnTick) { } + + @Override + public void framesSkipped(final float skippedCount) { + } }; + } diff --git a/core/src/com/etheller/warsmash/networking/WarsmashClient.java b/core/src/com/etheller/warsmash/networking/WarsmashClient.java index 1b02f68..77839af 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashClient.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashClient.java @@ -7,13 +7,13 @@ import java.util.HashMap; import java.util.Map; import com.badlogic.gdx.Gdx; -import com.etheller.warsmash.networking.udp.UdpClient; +import com.etheller.warsmash.networking.udp.OrderedUdpClient; import com.etheller.warsmash.util.WarsmashConstants; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerUnitOrderExecutor; public class WarsmashClient implements ServerToClientListener, GameTurnManager { - private final UdpClient udpClient; + private final OrderedUdpClient udpClient; private final War3MapViewer game; private final Map indexToExecutor = new HashMap<>(); private int latestCompletedTurn = -1; @@ -21,7 +21,8 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager { public WarsmashClient(final InetAddress serverAddress, final War3MapViewer game) throws UnknownHostException, IOException { - this.udpClient = new UdpClient(serverAddress, WarsmashConstants.PORT_NUMBER, new WarsmashClientParser(this)); + this.udpClient = new OrderedUdpClient(serverAddress, WarsmashConstants.PORT_NUMBER, + new WarsmashClientParser(this)); this.game = game; this.writer = new WarsmashClientWriter(this.udpClient); } @@ -131,12 +132,14 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager { } @Override - public int getLatestCompletedTurn() { - return this.latestCompletedTurn; + public void framesSkipped(final float skippedCount) { + this.writer.framesSkipped((int) skippedCount); + this.writer.send(); } - public UdpClient getUdpClient() { - return this.udpClient; + @Override + public int getLatestCompletedTurn() { + return this.latestCompletedTurn; } public WarsmashClientWriter getWriter() { diff --git a/core/src/com/etheller/warsmash/networking/WarsmashClientParser.java b/core/src/com/etheller/warsmash/networking/WarsmashClientParser.java index 45dd459..0a9e8d4 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashClientParser.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashClientParser.java @@ -2,15 +2,20 @@ package com.etheller.warsmash.networking; import java.nio.ByteBuffer; -import com.etheller.warsmash.networking.udp.UdpClientListener; +import com.etheller.warsmash.networking.udp.OrderedUdpClientListener; -public class WarsmashClientParser implements UdpClientListener { +public class WarsmashClientParser implements OrderedUdpClientListener { private final ServerToClientListener listener; public WarsmashClientParser(final ServerToClientListener listener) { this.listener = listener; } + @Override + public void cantReplay(final int seqNo) { + throw new IllegalStateException("Cant replay seqNo=" + seqNo + " !"); + } + @Override public void parse(final ByteBuffer buffer) { final int initialLimit = buffer.limit(); @@ -18,8 +23,9 @@ public class WarsmashClientParser implements UdpClientListener { while (buffer.hasRemaining()) { final int length = buffer.getInt(); if (length > buffer.remaining()) { - throw new IllegalStateException( - "Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!"); + // this packet is junk to us, so we will skip and continue (drop system will + // handle it) + System.err.println("Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!"); } final int protocol = buffer.getInt(); switch (protocol) { diff --git a/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java b/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java index 23f3c68..454032c 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java @@ -4,13 +4,13 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import com.etheller.warsmash.networking.udp.UdpClient; +import com.etheller.warsmash.networking.udp.OrderedUdpClient; public class WarsmashClientWriter { - private final UdpClient client; + private final OrderedUdpClient client; private final ByteBuffer sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN); - public WarsmashClientWriter(final UdpClient client) { + public WarsmashClientWriter(final OrderedUdpClient client) { this.client = client; } @@ -79,6 +79,13 @@ public class WarsmashClientWriter { this.sendBuffer.putInt(gameTurnTick); } + public void framesSkipped(final int skippedCount) { + this.sendBuffer.clear(); + this.sendBuffer.putInt(4 + 4); + this.sendBuffer.putInt(ClientToServerProtocol.FRAMES_SKIPPED); + this.sendBuffer.putInt(skippedCount); + } + public void joinGame() { this.sendBuffer.clear(); this.sendBuffer.putInt(4); @@ -87,7 +94,6 @@ public class WarsmashClientWriter { public void send() { this.sendBuffer.flip(); - System.out.println("CLIENT WRITER calling send() on " + this.sendBuffer.remaining() + " bytes"); try { this.client.send(this.sendBuffer); } diff --git a/core/src/com/etheller/warsmash/networking/WarsmashServer.java b/core/src/com/etheller/warsmash/networking/WarsmashServer.java index bd3fccf..568942f 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashServer.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashServer.java @@ -10,11 +10,11 @@ import java.util.Map; import java.util.Scanner; import java.util.Set; -import com.etheller.warsmash.networking.udp.UdpServer; +import com.etheller.warsmash.networking.udp.OrderedUdpServer; import com.etheller.warsmash.util.WarsmashConstants; public class WarsmashServer implements ClientToServerListener { - private final UdpServer udpServer; + private final OrderedUdpServer udpServer; private final Map socketAddressToPlayerIndex = new HashMap<>(); private final Set clientsAwaitingTurnFinished = new HashSet<>(); private final List turnActions = new ArrayList<>(); @@ -23,7 +23,7 @@ public class WarsmashServer implements ClientToServerListener { private boolean gameStarted = false; public WarsmashServer() throws IOException { - this.udpServer = new UdpServer(WarsmashConstants.PORT_NUMBER, new WarsmashServerParser(this)); + this.udpServer = new OrderedUdpServer(WarsmashConstants.PORT_NUMBER, new WarsmashServerParser(this)); this.writer = new WarsmashServerWriter(this.udpServer, this.socketAddressToPlayerIndex.keySet()); } @@ -139,7 +139,7 @@ public class WarsmashServer implements ClientToServerListener { @Override public void finishedTurn(final SocketAddress sourceAddress, final int gameTurnTick) { - System.out.println("finishedTurn(" + gameTurnTick + ") from " + sourceAddress); +// System.out.println("finishedTurn(" + gameTurnTick + ") from " + sourceAddress); if (!this.gameStarted) { throw new IllegalStateException( "Client should not send us finishedTurn() message when game has not started!"); @@ -160,6 +160,11 @@ public class WarsmashServer implements ClientToServerListener { } } + @Override + public void framesSkipped(final int nFramesSkipped) { + // dont care for now + } + public static void main(final String[] args) { try { final WarsmashServer server = new WarsmashServer(); diff --git a/core/src/com/etheller/warsmash/networking/WarsmashServerParser.java b/core/src/com/etheller/warsmash/networking/WarsmashServerParser.java index c99777c..4e7a1f7 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashServerParser.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashServerParser.java @@ -4,9 +4,9 @@ import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; -import com.etheller.warsmash.networking.udp.UdpServerListener; +import com.etheller.warsmash.networking.udp.OrderedUdpServerListener; -public class WarsmashServerParser implements UdpServerListener { +public class WarsmashServerParser implements OrderedUdpServerListener { private final ClientToServerListener listener; @@ -21,8 +21,10 @@ public class WarsmashServerParser implements UdpServerListener { while (buffer.hasRemaining()) { final int length = buffer.getInt(); if (length > buffer.remaining()) { - throw new IllegalStateException( - "Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!"); + // this packet is junk to us, so we will skip and continue (drop system will + // handle it) + System.err.println("Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!"); + break; } final int protocol = buffer.getInt(); switch (protocol) { @@ -81,6 +83,11 @@ public class WarsmashServerParser implements UdpServerListener { this.listener.joinGame(sourceAddress); break; } + case ClientToServerProtocol.FRAMES_SKIPPED: { + final int nFramesSkipped = buffer.getInt(); + this.listener.framesSkipped(nFramesSkipped); + break; + } default: System.err.println("Got unknown protocol: " + protocol); @@ -92,4 +99,9 @@ public class WarsmashServerParser implements UdpServerListener { buffer.position(initialLimit); } } + + @Override + public void cantReplay(final SocketAddress sourceAddress, final int seqNo) { + throw new IllegalStateException("Cant replay " + seqNo + " to " + sourceAddress + " !"); + } } diff --git a/core/src/com/etheller/warsmash/networking/WarsmashServerWriter.java b/core/src/com/etheller/warsmash/networking/WarsmashServerWriter.java index c62346b..e2b2f3a 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashServerWriter.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashServerWriter.java @@ -6,14 +6,14 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Set; -import com.etheller.warsmash.networking.udp.UdpServer; +import com.etheller.warsmash.networking.udp.OrderedUdpServer; public class WarsmashServerWriter implements ServerToClientListener { - private final UdpServer server; + private final OrderedUdpServer server; private final ByteBuffer sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN); private final Set allKnownAddressesToSend; - public WarsmashServerWriter(final UdpServer server, final Set allKnownAddressesToSend) { + public WarsmashServerWriter(final OrderedUdpServer server, final Set allKnownAddressesToSend) { this.server = server; this.allKnownAddressesToSend = allKnownAddressesToSend; } diff --git a/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClient.java b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClient.java new file mode 100644 index 0000000..8e7329e --- /dev/null +++ b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClient.java @@ -0,0 +1,31 @@ +package com.etheller.warsmash.networking.udp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +public class OrderedUdpClient extends OrderedUdpCommuncation implements Runnable { + private final UdpClient udpClient; + + public OrderedUdpClient(final InetAddress serverAddress, final int portNumber, + final OrderedUdpClientListener listener) throws UnknownHostException, IOException { + super(listener); + this.udpClient = new UdpClient(serverAddress, portNumber, this); + } + + @Override + protected void trySend(final ByteBuffer data) { + try { + this.udpClient.send(data); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void run() { + this.udpClient.run(); + } +} diff --git a/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClientListener.java b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClientListener.java new file mode 100644 index 0000000..cc4d0ee --- /dev/null +++ b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpClientListener.java @@ -0,0 +1,5 @@ +package com.etheller.warsmash.networking.udp; + +public interface OrderedUdpClientListener extends UdpClientListener { + void cantReplay(int seqNo); +} diff --git a/core/src/com/etheller/warsmash/networking/udp/OrderedUdpCommuncation.java b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpCommuncation.java new file mode 100644 index 0000000..c2c7185 --- /dev/null +++ b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpCommuncation.java @@ -0,0 +1,107 @@ +package com.etheller.warsmash.networking.udp; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; + +public abstract class OrderedUdpCommuncation implements UdpClientListener { + private static final int MAX_STORED_SENT_DATA_SIZE = 10000; + + private static final int ORDERED_UDP_MESSAGE = 'M'; + private static final int ORDERED_UDP_REPLAY_REQUEST = 'R'; + + private final Map seqNoToDataSent; + private final Map seqNoToDataReceived; + private final Queue seqNosStored; + private int nextSendSeqNo; + private int nextReceiveSeqNo; + private final OrderedUdpClientListener delegate; + private final ByteBuffer sendBuffer; + + public OrderedUdpCommuncation(final OrderedUdpClientListener delegate) { + this.seqNoToDataSent = new HashMap<>(); + this.seqNoToDataReceived = new HashMap<>(); + this.seqNosStored = new ArrayDeque<>(); + this.sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN); + this.delegate = delegate; + } + + public void send(final ByteBuffer data) throws IOException { + final ByteBuffer writeBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN); + writeBuffer.clear(); + final Integer seqNo = this.nextSendSeqNo; // only autobox once, would be ideal to not box at all + writeBuffer.putInt(ORDERED_UDP_MESSAGE); + writeBuffer.putInt(seqNo); + writeBuffer.put(data); + writeBuffer.flip(); + final int position = writeBuffer.position(); + trySend(writeBuffer); + writeBuffer.position(position); + if (this.seqNosStored.size() == MAX_STORED_SENT_DATA_SIZE) { + this.seqNoToDataSent.remove(this.seqNosStored.poll()); + } + this.seqNosStored.offer(seqNo); + this.seqNoToDataSent.put(seqNo, writeBuffer); + this.nextSendSeqNo++; + } + + // it's udp so we're just trying, we don't really know if it'll drop or not + protected abstract void trySend(final ByteBuffer data); + + @Override + public void parse(ByteBuffer readBuffer) { + final int messageType = readBuffer.getInt(); + switch (messageType) { + case ORDERED_UDP_MESSAGE: { + final int serverSeqNo = readBuffer.getInt(); + if (serverSeqNo > this.nextReceiveSeqNo) { + // ahead, need to queue it and request replay + for (int i = this.nextReceiveSeqNo; i < serverSeqNo; i++) { + requestReplay(i); + } + final ByteBuffer queuedReceivedData = ByteBuffer.allocate(readBuffer.remaining()) + .order(ByteOrder.BIG_ENDIAN); + queuedReceivedData.put(readBuffer); + this.seqNoToDataReceived.put(serverSeqNo, queuedReceivedData); + } + else if (serverSeqNo < this.nextReceiveSeqNo) { + // dup, ignore + } + else { + // exactly equal + do { + this.delegate.parse(readBuffer); + this.nextReceiveSeqNo++; + } + while ((readBuffer = this.seqNoToDataReceived.remove(this.nextReceiveSeqNo)) != null); + } + break; + } + case ORDERED_UDP_REPLAY_REQUEST: + final int requestedSeqNo = readBuffer.getInt(); + final ByteBuffer replayData = this.seqNoToDataSent.get(requestedSeqNo); + if (replayData == null) { + this.delegate.cantReplay(requestedSeqNo); + } + else { + System.err.println("Replay requested for packet with seqNo=" + requestedSeqNo + ", we will send it!"); + final int position = replayData.position(); + trySend(replayData); + replayData.position(position); + } + break; + } + } + + private void requestReplay(final int i) { + this.sendBuffer.clear(); + this.sendBuffer.putInt(ORDERED_UDP_REPLAY_REQUEST); + this.sendBuffer.putInt(i); + this.sendBuffer.flip(); + trySend(this.sendBuffer); + } +} diff --git a/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServer.java b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServer.java new file mode 100644 index 0000000..e973cc2 --- /dev/null +++ b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServer.java @@ -0,0 +1,81 @@ +package com.etheller.warsmash.networking.udp; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +public class OrderedUdpServer implements UdpServerListener, Runnable { + private final OrderedUdpServerListener listener; + private final UdpServer udpServer; + private final Map addrToClient = new HashMap<>(); + + public OrderedUdpServer(final int port, final OrderedUdpServerListener listener) throws IOException { + this.listener = listener; + this.udpServer = new UdpServer(port, this); + } + + @Override + public void parse(final SocketAddress sourceAddress, final ByteBuffer buffer) { + getClient(sourceAddress).parse(buffer); + } + + public void send(final SocketAddress destination, final ByteBuffer buffer) throws IOException { + getClient(destination).send(buffer); + } + + private OrderedKnownClient getClient(final SocketAddress sourceAddress) { + OrderedKnownClient orderedKnownClient = this.addrToClient.get(sourceAddress); + if (orderedKnownClient == null) { + orderedKnownClient = new OrderedKnownClient(sourceAddress, new OrderedAddressedSender(sourceAddress)); + this.addrToClient.put(sourceAddress, orderedKnownClient); + } + return orderedKnownClient; + } + + private class OrderedKnownClient extends OrderedUdpCommuncation { + + private final SocketAddress sourceAddress; + + public OrderedKnownClient(final SocketAddress sourceAddress, final OrderedUdpClientListener listener) { + super(listener); + this.sourceAddress = sourceAddress; + } + + @Override + protected void trySend(final ByteBuffer data) { + try { + OrderedUdpServer.this.udpServer.send(this.sourceAddress, data); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + } + + } + + private class OrderedAddressedSender implements OrderedUdpClientListener { + private final SocketAddress sourceAddress; + + public OrderedAddressedSender(final SocketAddress sourceAddress) { + this.sourceAddress = sourceAddress; + } + + @Override + public void parse(final ByteBuffer buffer) { + OrderedUdpServer.this.listener.parse(this.sourceAddress, buffer); + } + + @Override + public void cantReplay(final int seqNo) { + OrderedUdpServer.this.listener.cantReplay(this.sourceAddress, seqNo); + } + + } + + @Override + public void run() { + this.udpServer.run(); + } +} diff --git a/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServerListener.java b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServerListener.java new file mode 100644 index 0000000..64b5df9 --- /dev/null +++ b/core/src/com/etheller/warsmash/networking/udp/OrderedUdpServerListener.java @@ -0,0 +1,7 @@ +package com.etheller.warsmash.networking.udp; + +import java.net.SocketAddress; + +public interface OrderedUdpServerListener extends UdpServerListener { + void cantReplay(SocketAddress sourceAddress, int seqNo); +} diff --git a/core/src/com/etheller/warsmash/networking/udp/UdpClient.java b/core/src/com/etheller/warsmash/networking/udp/UdpClient.java index 58366cb..99ae5d0 100644 --- a/core/src/com/etheller/warsmash/networking/udp/UdpClient.java +++ b/core/src/com/etheller/warsmash/networking/udp/UdpClient.java @@ -12,7 +12,7 @@ import com.etheller.warsmash.util.WarsmashConstants; public class UdpClient implements Runnable { private final DatagramChannel channel; - private final ByteBuffer buffer; + private final ByteBuffer readBuffer; private boolean running; private final UdpClientListener clientListener; @@ -20,9 +20,9 @@ public class UdpClient implements Runnable { throws UnknownHostException, IOException { this.channel = DatagramChannel.open().connect(new InetSocketAddress(serverAddress, portNumber)); this.channel.configureBlocking(true); - this.buffer = ByteBuffer.allocate(1024); + this.readBuffer = ByteBuffer.allocate(1024); this.clientListener = clientListener; - this.buffer.order(ByteOrder.BIG_ENDIAN); + this.readBuffer.order(ByteOrder.BIG_ENDIAN); } public void send(final ByteBuffer data) throws IOException { @@ -38,10 +38,10 @@ public class UdpClient implements Runnable { this.running = true; while (this.running) { try { - this.buffer.clear(); - this.channel.receive(this.buffer); - this.buffer.flip(); - this.clientListener.parse(this.buffer); + this.readBuffer.clear(); + this.channel.receive(this.readBuffer); + this.readBuffer.flip(); + this.clientListener.parse(this.readBuffer); } catch (final IOException e) { System.err.println("Error reading from channel:"); diff --git a/core/src/com/etheller/warsmash/networking/udp/UdpServer.java b/core/src/com/etheller/warsmash/networking/udp/UdpServer.java index d656280..2d62c14 100644 --- a/core/src/com/etheller/warsmash/networking/udp/UdpServer.java +++ b/core/src/com/etheller/warsmash/networking/udp/UdpServer.java @@ -35,10 +35,6 @@ public class UdpServer implements Runnable { this.channel.send(buffer, destination); } - public void send(final ByteBuffer buffer) throws IOException { - this.channel.write(buffer); - } - @Override public void run() { this.running = true; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java index 3ba50bd..f20a67c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -683,7 +683,8 @@ public class War3MapViewer extends AbstractMdxModelViewer { final UnitSound constructingBuilding = War3MapViewer.this.uiSounds .getSound(War3MapViewer.this.gameUI.getSkinField("JobDoneSound")); final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(constructedStructure); - if (constructingBuilding != null) { + if ((constructingBuilding != null) && (renderUnit.getSimulationUnit() + .getPlayerIndex() == War3MapViewer.this.localPlayerIndex)) { constructingBuilding.play(War3MapViewer.this.worldScene.audioContext, constructedStructure.getX(), constructedStructure.getY(), renderUnit.getZ()); } @@ -1127,13 +1128,14 @@ public class War3MapViewer extends AbstractMdxModelViewer { final int playerIndex = unit.getPlayer(); final int customTeamColor = unit.getCustomTeamColor(); final float unitAngle = unit.getAngle(); - int editorConfigHitPointPercent = unit.getHitpoints(); + final int editorConfigHitPointPercent = unit.getHitpoints(); final CUnit unitCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, customTeamColor, unitAngle); - if(unitCreated != null) { + if (unitCreated != null) { if (editorConfigHitPointPercent > 0) { - unitCreated.setLife(simulation, unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f)); + unitCreated.setLife(this.simulation, + unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f)); } if (unit.getGoldAmount() != 0) { unitCreated.setGold(unit.getGoldAmount()); @@ -1495,6 +1497,10 @@ public class War3MapViewer extends AbstractMdxModelViewer { this.gameTurnManager.turnCompleted(this.simulation.getGameTurnTick()); } else { + if (this.updateTime > (WarsmashConstants.SIMULATION_STEP_TIME * 3)) { + this.gameTurnManager.framesSkipped(this.updateTime / WarsmashConstants.SIMULATION_STEP_TIME); + this.updateTime = 0; + } break; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java index df82d45..2c147cb 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java @@ -2646,6 +2646,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma rallied |= ability instanceof CAbilityRally; attacked |= ability instanceof CAbilityAttack; ordered = true; + break; } }