mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Update networking to include udp reset on drop packet
This commit is contained in:
parent
83fc753a0c
commit
08fd06a882
@ -53,7 +53,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI;
|
|||||||
|
|
||||||
public class WarsmashGdxMapScreen implements InputProcessor, Screen {
|
public class WarsmashGdxMapScreen implements InputProcessor, Screen {
|
||||||
public static final boolean ENABLE_AUDIO = true;
|
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 War3MapViewer viewer;
|
||||||
private final Rectangle tempRect = new Rectangle();
|
private final Rectangle tempRect = new Rectangle();
|
||||||
|
|
||||||
|
@ -21,4 +21,6 @@ public interface ClientToServerListener {
|
|||||||
|
|
||||||
void finishedTurn(SocketAddress sourceAddress, int gameTurnTick);
|
void finishedTurn(SocketAddress sourceAddress, int gameTurnTick);
|
||||||
|
|
||||||
|
void framesSkipped(int nFramesSkipped);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,5 @@ public class ClientToServerProtocol {
|
|||||||
public static final int UNIT_CANCEL_TRAINING = 5;
|
public static final int UNIT_CANCEL_TRAINING = 5;
|
||||||
public static final int FINISHED_TURN = 6;
|
public static final int FINISHED_TURN = 6;
|
||||||
public static final int JOIN_GAME = 7;
|
public static final int JOIN_GAME = 7;
|
||||||
|
public static final int FRAMES_SKIPPED = 8;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ public interface GameTurnManager {
|
|||||||
|
|
||||||
void turnCompleted(int gameTurnTick);
|
void turnCompleted(int gameTurnTick);
|
||||||
|
|
||||||
|
void framesSkipped(float skippedCount);
|
||||||
|
|
||||||
GameTurnManager PAUSED = new GameTurnManager() {
|
GameTurnManager PAUSED = new GameTurnManager() {
|
||||||
@Override
|
@Override
|
||||||
public int getLatestCompletedTurn() {
|
public int getLatestCompletedTurn() {
|
||||||
@ -15,6 +17,10 @@ public interface GameTurnManager {
|
|||||||
public void turnCompleted(final int gameTurnTick) {
|
public void turnCompleted(final int gameTurnTick) {
|
||||||
System.err.println("got turnCompleted(" + gameTurnTick + ") while paused !!");
|
System.err.println("got turnCompleted(" + gameTurnTick + ") while paused !!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void framesSkipped(final float skippedCount) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
GameTurnManager LOCAL = new GameTurnManager() {
|
GameTurnManager LOCAL = new GameTurnManager() {
|
||||||
@ -26,5 +32,10 @@ public interface GameTurnManager {
|
|||||||
@Override
|
@Override
|
||||||
public void turnCompleted(final int gameTurnTick) {
|
public void turnCompleted(final int gameTurnTick) {
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
@Override
|
||||||
|
public void framesSkipped(final float skippedCount) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.badlogic.gdx.Gdx;
|
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.util.WarsmashConstants;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerUnitOrderExecutor;
|
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerUnitOrderExecutor;
|
||||||
|
|
||||||
public class WarsmashClient implements ServerToClientListener, GameTurnManager {
|
public class WarsmashClient implements ServerToClientListener, GameTurnManager {
|
||||||
private final UdpClient udpClient;
|
private final OrderedUdpClient udpClient;
|
||||||
private final War3MapViewer game;
|
private final War3MapViewer game;
|
||||||
private final Map<Integer, CPlayerUnitOrderExecutor> indexToExecutor = new HashMap<>();
|
private final Map<Integer, CPlayerUnitOrderExecutor> indexToExecutor = new HashMap<>();
|
||||||
private int latestCompletedTurn = -1;
|
private int latestCompletedTurn = -1;
|
||||||
@ -21,7 +21,8 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
|
|||||||
|
|
||||||
public WarsmashClient(final InetAddress serverAddress, final War3MapViewer game)
|
public WarsmashClient(final InetAddress serverAddress, final War3MapViewer game)
|
||||||
throws UnknownHostException, IOException {
|
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.game = game;
|
||||||
this.writer = new WarsmashClientWriter(this.udpClient);
|
this.writer = new WarsmashClientWriter(this.udpClient);
|
||||||
}
|
}
|
||||||
@ -131,12 +132,14 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLatestCompletedTurn() {
|
public void framesSkipped(final float skippedCount) {
|
||||||
return this.latestCompletedTurn;
|
this.writer.framesSkipped((int) skippedCount);
|
||||||
|
this.writer.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UdpClient getUdpClient() {
|
@Override
|
||||||
return this.udpClient;
|
public int getLatestCompletedTurn() {
|
||||||
|
return this.latestCompletedTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WarsmashClientWriter getWriter() {
|
public WarsmashClientWriter getWriter() {
|
||||||
|
@ -2,15 +2,20 @@ package com.etheller.warsmash.networking;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
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;
|
private final ServerToClientListener listener;
|
||||||
|
|
||||||
public WarsmashClientParser(final ServerToClientListener listener) {
|
public WarsmashClientParser(final ServerToClientListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cantReplay(final int seqNo) {
|
||||||
|
throw new IllegalStateException("Cant replay seqNo=" + seqNo + " !");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(final ByteBuffer buffer) {
|
public void parse(final ByteBuffer buffer) {
|
||||||
final int initialLimit = buffer.limit();
|
final int initialLimit = buffer.limit();
|
||||||
@ -18,8 +23,9 @@ public class WarsmashClientParser implements UdpClientListener {
|
|||||||
while (buffer.hasRemaining()) {
|
while (buffer.hasRemaining()) {
|
||||||
final int length = buffer.getInt();
|
final int length = buffer.getInt();
|
||||||
if (length > buffer.remaining()) {
|
if (length > buffer.remaining()) {
|
||||||
throw new IllegalStateException(
|
// this packet is junk to us, so we will skip and continue (drop system will
|
||||||
"Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!");
|
// handle it)
|
||||||
|
System.err.println("Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!");
|
||||||
}
|
}
|
||||||
final int protocol = buffer.getInt();
|
final int protocol = buffer.getInt();
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
|
@ -4,13 +4,13 @@ import java.io.IOException;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import com.etheller.warsmash.networking.udp.UdpClient;
|
import com.etheller.warsmash.networking.udp.OrderedUdpClient;
|
||||||
|
|
||||||
public class WarsmashClientWriter {
|
public class WarsmashClientWriter {
|
||||||
private final UdpClient client;
|
private final OrderedUdpClient client;
|
||||||
private final ByteBuffer sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN);
|
private final ByteBuffer sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
public WarsmashClientWriter(final UdpClient client) {
|
public WarsmashClientWriter(final OrderedUdpClient client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +79,13 @@ public class WarsmashClientWriter {
|
|||||||
this.sendBuffer.putInt(gameTurnTick);
|
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() {
|
public void joinGame() {
|
||||||
this.sendBuffer.clear();
|
this.sendBuffer.clear();
|
||||||
this.sendBuffer.putInt(4);
|
this.sendBuffer.putInt(4);
|
||||||
@ -87,7 +94,6 @@ public class WarsmashClientWriter {
|
|||||||
|
|
||||||
public void send() {
|
public void send() {
|
||||||
this.sendBuffer.flip();
|
this.sendBuffer.flip();
|
||||||
System.out.println("CLIENT WRITER calling send() on " + this.sendBuffer.remaining() + " bytes");
|
|
||||||
try {
|
try {
|
||||||
this.client.send(this.sendBuffer);
|
this.client.send(this.sendBuffer);
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ import java.util.Map;
|
|||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.etheller.warsmash.networking.udp.UdpServer;
|
import com.etheller.warsmash.networking.udp.OrderedUdpServer;
|
||||||
import com.etheller.warsmash.util.WarsmashConstants;
|
import com.etheller.warsmash.util.WarsmashConstants;
|
||||||
|
|
||||||
public class WarsmashServer implements ClientToServerListener {
|
public class WarsmashServer implements ClientToServerListener {
|
||||||
private final UdpServer udpServer;
|
private final OrderedUdpServer udpServer;
|
||||||
private final Map<SocketAddress, Integer> socketAddressToPlayerIndex = new HashMap<>();
|
private final Map<SocketAddress, Integer> socketAddressToPlayerIndex = new HashMap<>();
|
||||||
private final Set<SocketAddress> clientsAwaitingTurnFinished = new HashSet<>();
|
private final Set<SocketAddress> clientsAwaitingTurnFinished = new HashSet<>();
|
||||||
private final List<Runnable> turnActions = new ArrayList<>();
|
private final List<Runnable> turnActions = new ArrayList<>();
|
||||||
@ -23,7 +23,7 @@ public class WarsmashServer implements ClientToServerListener {
|
|||||||
private boolean gameStarted = false;
|
private boolean gameStarted = false;
|
||||||
|
|
||||||
public WarsmashServer() throws IOException {
|
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());
|
this.writer = new WarsmashServerWriter(this.udpServer, this.socketAddressToPlayerIndex.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ public class WarsmashServer implements ClientToServerListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void finishedTurn(final SocketAddress sourceAddress, final int gameTurnTick) {
|
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) {
|
if (!this.gameStarted) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Client should not send us finishedTurn() message when game has not started!");
|
"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) {
|
public static void main(final String[] args) {
|
||||||
try {
|
try {
|
||||||
final WarsmashServer server = new WarsmashServer();
|
final WarsmashServer server = new WarsmashServer();
|
||||||
|
@ -4,9 +4,9 @@ import java.io.IOException;
|
|||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
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;
|
private final ClientToServerListener listener;
|
||||||
|
|
||||||
@ -21,8 +21,10 @@ public class WarsmashServerParser implements UdpServerListener {
|
|||||||
while (buffer.hasRemaining()) {
|
while (buffer.hasRemaining()) {
|
||||||
final int length = buffer.getInt();
|
final int length = buffer.getInt();
|
||||||
if (length > buffer.remaining()) {
|
if (length > buffer.remaining()) {
|
||||||
throw new IllegalStateException(
|
// this packet is junk to us, so we will skip and continue (drop system will
|
||||||
"Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!");
|
// handle it)
|
||||||
|
System.err.println("Got mismatched protocol length " + length + " > " + buffer.remaining() + "!!");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
final int protocol = buffer.getInt();
|
final int protocol = buffer.getInt();
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
@ -81,6 +83,11 @@ public class WarsmashServerParser implements UdpServerListener {
|
|||||||
this.listener.joinGame(sourceAddress);
|
this.listener.joinGame(sourceAddress);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ClientToServerProtocol.FRAMES_SKIPPED: {
|
||||||
|
final int nFramesSkipped = buffer.getInt();
|
||||||
|
this.listener.framesSkipped(nFramesSkipped);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
System.err.println("Got unknown protocol: " + protocol);
|
System.err.println("Got unknown protocol: " + protocol);
|
||||||
@ -92,4 +99,9 @@ public class WarsmashServerParser implements UdpServerListener {
|
|||||||
buffer.position(initialLimit);
|
buffer.position(initialLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cantReplay(final SocketAddress sourceAddress, final int seqNo) {
|
||||||
|
throw new IllegalStateException("Cant replay " + seqNo + " to " + sourceAddress + " !");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,14 +6,14 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.etheller.warsmash.networking.udp.UdpServer;
|
import com.etheller.warsmash.networking.udp.OrderedUdpServer;
|
||||||
|
|
||||||
public class WarsmashServerWriter implements ServerToClientListener {
|
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 ByteBuffer sendBuffer = ByteBuffer.allocate(1024).order(ByteOrder.BIG_ENDIAN);
|
||||||
private final Set<SocketAddress> allKnownAddressesToSend;
|
private final Set<SocketAddress> allKnownAddressesToSend;
|
||||||
|
|
||||||
public WarsmashServerWriter(final UdpServer server, final Set<SocketAddress> allKnownAddressesToSend) {
|
public WarsmashServerWriter(final OrderedUdpServer server, final Set<SocketAddress> allKnownAddressesToSend) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.allKnownAddressesToSend = allKnownAddressesToSend;
|
this.allKnownAddressesToSend = allKnownAddressesToSend;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.etheller.warsmash.networking.udp;
|
||||||
|
|
||||||
|
public interface OrderedUdpClientListener extends UdpClientListener {
|
||||||
|
void cantReplay(int seqNo);
|
||||||
|
}
|
@ -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<Integer, ByteBuffer> seqNoToDataSent;
|
||||||
|
private final Map<Integer, ByteBuffer> seqNoToDataReceived;
|
||||||
|
private final Queue<Integer> 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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<SocketAddress, OrderedKnownClient> 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();
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -12,7 +12,7 @@ import com.etheller.warsmash.util.WarsmashConstants;
|
|||||||
|
|
||||||
public class UdpClient implements Runnable {
|
public class UdpClient implements Runnable {
|
||||||
private final DatagramChannel channel;
|
private final DatagramChannel channel;
|
||||||
private final ByteBuffer buffer;
|
private final ByteBuffer readBuffer;
|
||||||
private boolean running;
|
private boolean running;
|
||||||
private final UdpClientListener clientListener;
|
private final UdpClientListener clientListener;
|
||||||
|
|
||||||
@ -20,9 +20,9 @@ public class UdpClient implements Runnable {
|
|||||||
throws UnknownHostException, IOException {
|
throws UnknownHostException, IOException {
|
||||||
this.channel = DatagramChannel.open().connect(new InetSocketAddress(serverAddress, portNumber));
|
this.channel = DatagramChannel.open().connect(new InetSocketAddress(serverAddress, portNumber));
|
||||||
this.channel.configureBlocking(true);
|
this.channel.configureBlocking(true);
|
||||||
this.buffer = ByteBuffer.allocate(1024);
|
this.readBuffer = ByteBuffer.allocate(1024);
|
||||||
this.clientListener = clientListener;
|
this.clientListener = clientListener;
|
||||||
this.buffer.order(ByteOrder.BIG_ENDIAN);
|
this.readBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(final ByteBuffer data) throws IOException {
|
public void send(final ByteBuffer data) throws IOException {
|
||||||
@ -38,10 +38,10 @@ public class UdpClient implements Runnable {
|
|||||||
this.running = true;
|
this.running = true;
|
||||||
while (this.running) {
|
while (this.running) {
|
||||||
try {
|
try {
|
||||||
this.buffer.clear();
|
this.readBuffer.clear();
|
||||||
this.channel.receive(this.buffer);
|
this.channel.receive(this.readBuffer);
|
||||||
this.buffer.flip();
|
this.readBuffer.flip();
|
||||||
this.clientListener.parse(this.buffer);
|
this.clientListener.parse(this.readBuffer);
|
||||||
}
|
}
|
||||||
catch (final IOException e) {
|
catch (final IOException e) {
|
||||||
System.err.println("Error reading from channel:");
|
System.err.println("Error reading from channel:");
|
||||||
|
@ -35,10 +35,6 @@ public class UdpServer implements Runnable {
|
|||||||
this.channel.send(buffer, destination);
|
this.channel.send(buffer, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(final ByteBuffer buffer) throws IOException {
|
|
||||||
this.channel.write(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
|
@ -683,7 +683,8 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
|||||||
final UnitSound constructingBuilding = War3MapViewer.this.uiSounds
|
final UnitSound constructingBuilding = War3MapViewer.this.uiSounds
|
||||||
.getSound(War3MapViewer.this.gameUI.getSkinField("JobDoneSound"));
|
.getSound(War3MapViewer.this.gameUI.getSkinField("JobDoneSound"));
|
||||||
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(constructedStructure);
|
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,
|
constructingBuilding.play(War3MapViewer.this.worldScene.audioContext,
|
||||||
constructedStructure.getX(), constructedStructure.getY(), renderUnit.getZ());
|
constructedStructure.getX(), constructedStructure.getY(), renderUnit.getZ());
|
||||||
}
|
}
|
||||||
@ -1127,13 +1128,14 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
|||||||
final int playerIndex = unit.getPlayer();
|
final int playerIndex = unit.getPlayer();
|
||||||
final int customTeamColor = unit.getCustomTeamColor();
|
final int customTeamColor = unit.getCustomTeamColor();
|
||||||
final float unitAngle = unit.getAngle();
|
final float unitAngle = unit.getAngle();
|
||||||
int editorConfigHitPointPercent = unit.getHitpoints();
|
final int editorConfigHitPointPercent = unit.getHitpoints();
|
||||||
|
|
||||||
final CUnit unitCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex,
|
final CUnit unitCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex,
|
||||||
customTeamColor, unitAngle);
|
customTeamColor, unitAngle);
|
||||||
if (unitCreated != null) {
|
if (unitCreated != null) {
|
||||||
if (editorConfigHitPointPercent > 0) {
|
if (editorConfigHitPointPercent > 0) {
|
||||||
unitCreated.setLife(simulation, unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f));
|
unitCreated.setLife(this.simulation,
|
||||||
|
unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f));
|
||||||
}
|
}
|
||||||
if (unit.getGoldAmount() != 0) {
|
if (unit.getGoldAmount() != 0) {
|
||||||
unitCreated.setGold(unit.getGoldAmount());
|
unitCreated.setGold(unit.getGoldAmount());
|
||||||
@ -1495,6 +1497,10 @@ public class War3MapViewer extends AbstractMdxModelViewer {
|
|||||||
this.gameTurnManager.turnCompleted(this.simulation.getGameTurnTick());
|
this.gameTurnManager.turnCompleted(this.simulation.getGameTurnTick());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (this.updateTime > (WarsmashConstants.SIMULATION_STEP_TIME * 3)) {
|
||||||
|
this.gameTurnManager.framesSkipped(this.updateTime / WarsmashConstants.SIMULATION_STEP_TIME);
|
||||||
|
this.updateTime = 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2646,6 +2646,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
|
|||||||
rallied |= ability instanceof CAbilityRally;
|
rallied |= ability instanceof CAbilityRally;
|
||||||
attacked |= ability instanceof CAbilityAttack;
|
attacked |= ability instanceof CAbilityAttack;
|
||||||
ordered = true;
|
ordered = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user