diff --git a/core/gen/com/riiablo/net/packet/d2gs/D2GSData.java b/core/gen/com/riiablo/net/packet/d2gs/D2GSData.java index 42917d4d..37981f54 100644 --- a/core/gen/com/riiablo/net/packet/d2gs/D2GSData.java +++ b/core/gen/com/riiablo/net/packet/d2gs/D2GSData.java @@ -11,20 +11,21 @@ public final class D2GSData { public static final byte RunToEntity = 4; public static final byte Connection = 5; public static final byte Disconnect = 6; - public static final byte EntitySync = 7; - public static final byte GroundToCursor = 8; - public static final byte CursorToGround = 9; - public static final byte StoreToCursor = 10; - public static final byte CursorToStore = 11; - public static final byte SwapStoreItem = 12; - public static final byte BodyToCursor = 13; - public static final byte CursorToBody = 14; - public static final byte SwapBodyItem = 15; - public static final byte BeltToCursor = 16; - public static final byte CursorToBelt = 17; - public static final byte SwapBeltItem = 18; + public static final byte Ping = 7; + public static final byte EntitySync = 8; + public static final byte GroundToCursor = 9; + public static final byte CursorToGround = 10; + public static final byte StoreToCursor = 11; + public static final byte CursorToStore = 12; + public static final byte SwapStoreItem = 13; + public static final byte BodyToCursor = 14; + public static final byte CursorToBody = 15; + public static final byte SwapBodyItem = 16; + public static final byte BeltToCursor = 17; + public static final byte CursorToBelt = 18; + public static final byte SwapBeltItem = 19; - public static final String[] names = { "NONE", "WalkToLocation", "WalkToEntity", "RunToLocation", "RunToEntity", "Connection", "Disconnect", "EntitySync", "GroundToCursor", "CursorToGround", "StoreToCursor", "CursorToStore", "SwapStoreItem", "BodyToCursor", "CursorToBody", "SwapBodyItem", "BeltToCursor", "CursorToBelt", "SwapBeltItem", }; + public static final String[] names = { "NONE", "WalkToLocation", "WalkToEntity", "RunToLocation", "RunToEntity", "Connection", "Disconnect", "Ping", "EntitySync", "GroundToCursor", "CursorToGround", "StoreToCursor", "CursorToStore", "SwapStoreItem", "BodyToCursor", "CursorToBody", "SwapBodyItem", "BeltToCursor", "CursorToBelt", "SwapBeltItem", }; public static String name(int e) { return names[e]; } } diff --git a/core/gen/com/riiablo/net/packet/d2gs/Ping.java b/core/gen/com/riiablo/net/packet/d2gs/Ping.java new file mode 100644 index 00000000..3021ae87 --- /dev/null +++ b/core/gen/com/riiablo/net/packet/d2gs/Ping.java @@ -0,0 +1,37 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +package com.riiablo.net.packet.d2gs; + +import java.nio.*; +import java.lang.*; +import java.util.*; +import com.google.flatbuffers.*; + +@SuppressWarnings("unused") +public final class Ping extends Table { + public static Ping getRootAsPing(ByteBuffer _bb) { return getRootAsPing(_bb, new Ping()); } + public static Ping getRootAsPing(ByteBuffer _bb, Ping obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__assign(_bb.getInt(_bb.position()) + _bb.position(), _bb)); } + public void __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; vtable_start = bb_pos - bb.getInt(bb_pos); vtable_size = bb.getShort(vtable_start); } + public Ping __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; } + + public int tickCount() { int o = __offset(4); return o != 0 ? bb.getInt(o + bb_pos) : 0; } + public long time() { int o = __offset(6); return o != 0 ? bb.getLong(o + bb_pos) : 0L; } + + public static int createPing(FlatBufferBuilder builder, + int tickCount, + long time) { + builder.startObject(2); + Ping.addTime(builder, time); + Ping.addTickCount(builder, tickCount); + return Ping.endPing(builder); + } + + public static void startPing(FlatBufferBuilder builder) { builder.startObject(2); } + public static void addTickCount(FlatBufferBuilder builder, int tickCount) { builder.addInt(0, tickCount, 0); } + public static void addTime(FlatBufferBuilder builder, long time) { builder.addLong(1, time, 0L); } + public static int endPing(FlatBufferBuilder builder) { + int o = builder.endObject(); + return o; + } +} + diff --git a/core/src/com/riiablo/Client.java b/core/src/com/riiablo/Client.java index 4b216eed..e9daff2d 100644 --- a/core/src/com/riiablo/Client.java +++ b/core/src/com/riiablo/Client.java @@ -441,7 +441,12 @@ public class Client extends Game { BitmapFont font = console.getFont(); if (font == null) return; - fps.setText(font, Integer.toString(Gdx.graphics.getFramesPerSecond())); + StringBuilder builder = new StringBuilder(64); + builder + .append(Gdx.graphics.getFramesPerSecond()) + .append('\n').append(Riiablo.ping).append(" ms") + ; + fps.setText(font, builder.toString()); int drawFpsMethod = this.drawFpsMethod; if (forceDrawFps && drawFpsMethod == FPS_NONE) { drawFpsMethod = FPS_TOPLEFT; diff --git a/core/src/com/riiablo/Riiablo.java b/core/src/com/riiablo/Riiablo.java index 90a3995b..2a8be66f 100644 --- a/core/src/com/riiablo/Riiablo.java +++ b/core/src/com/riiablo/Riiablo.java @@ -67,4 +67,5 @@ public class Riiablo { public static World engine; public static GameScreen game; public static D2 anim; + public static long ping; } diff --git a/core/src/com/riiablo/engine/client/ClientNetworkReceiver.java b/core/src/com/riiablo/engine/client/ClientNetworkReceiver.java index fb31cf17..e1d4df36 100644 --- a/core/src/com/riiablo/engine/client/ClientNetworkReceiver.java +++ b/core/src/com/riiablo/engine/client/ClientNetworkReceiver.java @@ -54,6 +54,7 @@ import com.riiablo.net.packet.d2gs.EntitySync; import com.riiablo.net.packet.d2gs.GroundToCursor; import com.riiablo.net.packet.d2gs.ItemP; import com.riiablo.net.packet.d2gs.MonsterP; +import com.riiablo.net.packet.d2gs.Ping; import com.riiablo.net.packet.d2gs.PlayerP; import com.riiablo.net.packet.d2gs.PositionP; import com.riiablo.net.packet.d2gs.StoreToCursor; @@ -97,6 +98,7 @@ public class ClientNetworkReceiver extends IntervalSystem { protected CofManager cofs; protected NetworkIdManager syncIds; protected ItemManager items; + protected Pinger pinger; @Wire(name="client.socket") protected Socket socket; @@ -150,6 +152,9 @@ public class ClientNetworkReceiver extends IntervalSystem { case D2GSData.Disconnect: Disconnect(packet); break; + case D2GSData.Ping: + pinger.Ping((Ping) packet.data(new Ping())); + break; case D2GSData.EntitySync: Synchronize(packet); break; diff --git a/core/src/com/riiablo/engine/client/Pinger.java b/core/src/com/riiablo/engine/client/Pinger.java new file mode 100644 index 00000000..e1d07249 --- /dev/null +++ b/core/src/com/riiablo/engine/client/Pinger.java @@ -0,0 +1,59 @@ +package com.riiablo.engine.client; + +import com.google.flatbuffers.FlatBufferBuilder; + +import com.artemis.annotations.All; +import com.artemis.annotations.Wire; +import com.artemis.systems.IntervalSystem; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.net.Socket; +import com.badlogic.gdx.utils.TimeUtils; +import com.riiablo.Riiablo; +import com.riiablo.net.packet.d2gs.D2GS; +import com.riiablo.net.packet.d2gs.D2GSData; +import com.riiablo.net.packet.d2gs.Ping; + +import java.io.OutputStream; +import java.nio.channels.Channels; +import java.nio.channels.WritableByteChannel; + +@All +public class Pinger extends IntervalSystem { + private static final String TAG = "Pinger"; + private static final boolean DEBUG = !true; + + @Wire(name = "client.socket") + protected Socket socket; + + // TODO: this system depends on a 32-bit int -- at some point will need to figure out if this will become an issue + // it may be possible to use Gdx.graphics.getFrameId() -- but that isn't related to engine tick + private int tick; + +// TODO: provide a running average of past N RTTs +// private final double deltas[] = new double[5]; +// private int deltaCount; + + public Pinger() { + super(null, 1.0f); + } + + @Override + protected void processSystem() { + FlatBufferBuilder builder = new FlatBufferBuilder(0); + int dataOffset = Ping.createPing(builder, tick++, TimeUtils.millis()); + int root = D2GS.createD2GS(builder, D2GSData.Ping, dataOffset); + D2GS.finishSizePrefixedD2GSBuffer(builder, root); + + try { + OutputStream out = socket.getOutputStream(); + WritableByteChannel channelOut = Channels.newChannel(out); + channelOut.write(builder.dataBuffer()); + } catch (Throwable t) { + Gdx.app.error(TAG, t.getMessage(), t); + } + } + + public void Ping(Ping packet) { + Riiablo.ping = TimeUtils.timeSinceMillis(packet.time()); + } +} diff --git a/core/src/com/riiablo/net/d2gs/D2GS.fbs b/core/src/com/riiablo/net/d2gs/D2GS.fbs index 8e6d6382..9f1d653d 100644 --- a/core/src/com/riiablo/net/d2gs/D2GS.fbs +++ b/core/src/com/riiablo/net/d2gs/D2GS.fbs @@ -12,6 +12,7 @@ union D2GSData { RunToEntity, Connection, Disconnect, + Ping, EntitySync, GroundToCursor, CursorToGround, diff --git a/core/src/com/riiablo/net/d2gs/Networking.fbs b/core/src/com/riiablo/net/d2gs/Networking.fbs index 819be16d..1c213b4d 100644 --- a/core/src/com/riiablo/net/d2gs/Networking.fbs +++ b/core/src/com/riiablo/net/d2gs/Networking.fbs @@ -17,5 +17,13 @@ table Disconnect { // request entityId:int32; + // response +} + +table Ping { + // request + tickCount:int32; + time:int64; + // response } \ No newline at end of file diff --git a/core/src/com/riiablo/screen/NetworkedGameScreen.java b/core/src/com/riiablo/screen/NetworkedGameScreen.java index 676b0fb6..20fc0652 100644 --- a/core/src/com/riiablo/screen/NetworkedGameScreen.java +++ b/core/src/com/riiablo/screen/NetworkedGameScreen.java @@ -2,9 +2,10 @@ package com.riiablo.screen; import com.artemis.WorldConfigurationBuilder; import com.badlogic.gdx.net.Socket; -import com.riiablo.save.CharData; import com.riiablo.engine.client.ClientNetworkReceiver; import com.riiablo.engine.client.ClientNetworkSyncronizer; +import com.riiablo.engine.client.Pinger; +import com.riiablo.save.CharData; public class NetworkedGameScreen extends GameScreen { private static final String TAG = "NetworkedGameScreen"; @@ -22,6 +23,7 @@ public class NetworkedGameScreen extends GameScreen { WorldConfigurationBuilder builder = super.getWorldConfigurationBuilder(); builder.with(WorldConfigurationBuilder.Priority.HIGH, new ClientNetworkReceiver()); builder.with(new ClientNetworkSyncronizer()); + builder.with(new Pinger()); return builder; } diff --git a/server/d2gs/src/com/riiablo/server/d2gs/D2GS.java b/server/d2gs/src/com/riiablo/server/d2gs/D2GS.java index 9626a085..8c466b5b 100644 --- a/server/d2gs/src/com/riiablo/server/d2gs/D2GS.java +++ b/server/d2gs/src/com/riiablo/server/d2gs/D2GS.java @@ -427,6 +427,9 @@ public class D2GS extends ApplicationAdapter { case D2GSData.SwapBeltItem: SwapBeltItem(packet); break; + case D2GSData.Ping: + Ping(packet); + break; default: Gdx.app.error(TAG, "Unknown packet type: " + packet.data.dataType()); } @@ -531,6 +534,12 @@ public class D2GS extends ApplicationAdapter { } } + private void Ping(Packet packet) { +// Ping ping = (Ping) packet.data.data(new Ping()); + packet.id = (1 << packet.id); + outPackets.offer(packet); + } + private void Synchronize(Packet packet) { int entityId = player.get(packet.id, Engine.INVALID_ENTITY); assert entityId != Engine.INVALID_ENTITY;