mirror of
https://bitbucket.org/projectswg/forwarder.git
synced 2026-01-16 23:04:26 -05:00
Removed the outbound packet rate tuning, added dual protocol stacks with smart routing
This commit is contained in:
@@ -128,6 +128,8 @@ public class Forwarder {
|
||||
private String password = null;
|
||||
private int loginPort = 0;
|
||||
private int zonePort = 0;
|
||||
private int outboundTunerMaxSend = 100;
|
||||
private int outboundTunerInterval = 20;
|
||||
|
||||
private ForwarderData() { }
|
||||
|
||||
@@ -151,6 +153,14 @@ public class Forwarder {
|
||||
return zonePort;
|
||||
}
|
||||
|
||||
public int getOutboundTunerMaxSend() {
|
||||
return outboundTunerMaxSend;
|
||||
}
|
||||
|
||||
public int getOutboundTunerInterval() {
|
||||
return outboundTunerInterval;
|
||||
}
|
||||
|
||||
public void setAddress(InetSocketAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
@@ -170,6 +180,15 @@ public class Forwarder {
|
||||
public void setZonePort(int zonePort) {
|
||||
this.zonePort = zonePort;
|
||||
}
|
||||
|
||||
public void setOutboundTunerMaxSend(int outboundTunerMaxSend) {
|
||||
this.outboundTunerMaxSend = outboundTunerMaxSend;
|
||||
}
|
||||
|
||||
public void setOutboundTunerInterval(int outboundTunerInterval) {
|
||||
this.outboundTunerInterval = outboundTunerInterval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
package com.projectswg.forwarder.intents.client;
|
||||
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import com.projectswg.forwarder.resources.networking.packets.Packet;
|
||||
import me.joshlarson.jlcommon.control.Intent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SonyPacketInboundIntent extends Intent {
|
||||
|
||||
private final ProtocolStack stack;
|
||||
private final Packet packet;
|
||||
|
||||
public SonyPacketInboundIntent(@NotNull Packet packet) {
|
||||
public SonyPacketInboundIntent(@NotNull ProtocolStack stack, @NotNull Packet packet) {
|
||||
this.stack = stack;
|
||||
this.packet = packet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ProtocolStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Packet getPacket() {
|
||||
return packet;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/***********************************************************************************
|
||||
* Copyright (C) 2018 /// Project SWG /// www.projectswg.com *
|
||||
* *
|
||||
* This file is part of the ProjectSWG Launcher. *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***********************************************************************************/
|
||||
|
||||
package com.projectswg.forwarder.intents.client;
|
||||
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import me.joshlarson.jlcommon.control.Intent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StackCreatedIntent extends Intent {
|
||||
|
||||
private final ProtocolStack stack;
|
||||
|
||||
public StackCreatedIntent(ProtocolStack stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProtocolStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/***********************************************************************************
|
||||
* Copyright (C) 2018 /// Project SWG /// www.projectswg.com *
|
||||
* *
|
||||
* This file is part of the ProjectSWG Launcher. *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***********************************************************************************/
|
||||
|
||||
package com.projectswg.forwarder.intents.client;
|
||||
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import me.joshlarson.jlcommon.control.Intent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class StackDestroyedIntent extends Intent {
|
||||
|
||||
private final ProtocolStack stack;
|
||||
|
||||
public StackDestroyedIntent(ProtocolStack stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProtocolStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.projectswg.forwarder.intents.client;
|
||||
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import me.joshlarson.jlcommon.control.Intent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UpdateStackIntent extends Intent {
|
||||
|
||||
private final ProtocolStack stack;
|
||||
|
||||
public UpdateStackIntent(ProtocolStack stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProtocolStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.projectswg.forwarder.resources.client.state;
|
||||
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
public class OutboundDataTuner {
|
||||
|
||||
private long start;
|
||||
private int maxSend;
|
||||
private int outOfOrders;
|
||||
|
||||
public OutboundDataTuner() {
|
||||
reset();
|
||||
}
|
||||
|
||||
public int getMaxSend() {
|
||||
return maxSend;
|
||||
}
|
||||
|
||||
public void markStart() {
|
||||
reset();
|
||||
start = System.nanoTime();
|
||||
}
|
||||
|
||||
public void markEnd() {
|
||||
long time = System.nanoTime() - start;
|
||||
Log.d("Max Send=%d Time to Zone=%.0fms OOO=%d", maxSend, time/1E6, outOfOrders);
|
||||
reset();
|
||||
}
|
||||
|
||||
public void markOOO() {
|
||||
outOfOrders++;
|
||||
maxSend -= 2;
|
||||
capMaxSend();
|
||||
}
|
||||
|
||||
public void markAck() {
|
||||
maxSend += 2;
|
||||
capMaxSend();
|
||||
}
|
||||
|
||||
public void markLag() {
|
||||
maxSend -= 50;
|
||||
capMaxSend();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
start = 0;
|
||||
outOfOrders = 0;
|
||||
maxSend = 200;
|
||||
}
|
||||
|
||||
private void capMaxSend() {
|
||||
if (maxSend < 100)
|
||||
maxSend = 100;
|
||||
else if (maxSend > 5000)
|
||||
maxSend = 5000;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import com.projectswg.common.network.packets.PacketType;
|
||||
import com.projectswg.common.network.packets.swg.zone.HeartBeat;
|
||||
import com.projectswg.forwarder.intents.client.DataPacketInboundIntent;
|
||||
import com.projectswg.forwarder.intents.client.SonyPacketInboundIntent;
|
||||
import com.projectswg.forwarder.intents.client.UpdateStackIntent;
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import com.projectswg.forwarder.resources.networking.packets.*;
|
||||
import me.joshlarson.jlcommon.control.IntentChain;
|
||||
@@ -16,30 +15,20 @@ import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ClientInboundDataService extends Service {
|
||||
|
||||
private final IntentMultiplexer multiplexer;
|
||||
private final AtomicReference<ProtocolStack> stack;
|
||||
private final IntentChain intentChain;
|
||||
|
||||
public ClientInboundDataService() {
|
||||
this.multiplexer = new IntentMultiplexer(this, ProtocolStack.class, Packet.class);
|
||||
this.stack = new AtomicReference<>(null);
|
||||
this.intentChain = new IntentChain();
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleSonyPacketInboundIntent(SonyPacketInboundIntent spii) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
assert stack != null : "stack is null";
|
||||
multiplexer.call(stack, spii.getPacket());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleUpdateStackIntent(UpdateStackIntent sci) {
|
||||
stack.set(sci.getStack());
|
||||
multiplexer.call(spii.getStack(), spii.getPacket());
|
||||
}
|
||||
|
||||
@Multiplexer
|
||||
|
||||
@@ -3,8 +3,10 @@ package com.projectswg.forwarder.services.client;
|
||||
import com.projectswg.common.network.NetBuffer;
|
||||
import com.projectswg.common.network.packets.PacketType;
|
||||
import com.projectswg.common.network.packets.swg.zone.HeartBeat;
|
||||
import com.projectswg.forwarder.Forwarder.ForwarderData;
|
||||
import com.projectswg.forwarder.intents.client.*;
|
||||
import com.projectswg.forwarder.resources.client.state.OutboundDataTuner;
|
||||
import com.projectswg.forwarder.intents.control.StartForwarderIntent;
|
||||
import com.projectswg.forwarder.resources.networking.ClientServer;
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import com.projectswg.forwarder.resources.networking.data.SequencedOutbound;
|
||||
import com.projectswg.forwarder.resources.networking.packets.Acknowledge;
|
||||
@@ -20,39 +22,51 @@ import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ClientOutboundDataService extends Service {
|
||||
|
||||
private static final int TIMER_DELAY = 20;
|
||||
|
||||
private final IntentMultiplexer multiplexer;
|
||||
private final AtomicReference<ProtocolStack> stack;
|
||||
private final Set<ProtocolStack> activeStacks;
|
||||
private final ScheduledThreadPool timerThread;
|
||||
private final OutboundDataTuner tuner;
|
||||
private final Object outboundMutex;
|
||||
|
||||
private ForwarderData data;
|
||||
|
||||
public ClientOutboundDataService() {
|
||||
this.multiplexer = new IntentMultiplexer(this, ProtocolStack.class, Packet.class);
|
||||
this.stack = new AtomicReference<>(null);
|
||||
this.activeStacks = ConcurrentHashMap.newKeySet();
|
||||
this.timerThread = new ScheduledThreadPool(2, 5, "outbound-sender-%d");
|
||||
this.tuner = new OutboundDataTuner();
|
||||
this.outboundMutex = new Object();
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean terminate() {
|
||||
timerThread.stop();
|
||||
return timerThread.awaitTermination(1000);
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleStartForwarderIntent(StartForwarderIntent sfi) {
|
||||
data = sfi.getData();
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleSonyPacketInboundIntent(SonyPacketInboundIntent spii) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
assert stack != null : "stack is null";
|
||||
multiplexer.call(stack, spii.getPacket());
|
||||
multiplexer.call(spii.getStack(), spii.getPacket());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleClientConnectedIntent(ClientConnectedIntent cci) {
|
||||
if (timerThread.isRunning())
|
||||
return;
|
||||
int interval = data.getOutboundTunerInterval();
|
||||
if (interval <= 0)
|
||||
interval = 20;
|
||||
timerThread.start();
|
||||
timerThread.executeWithFixedDelay(TIMER_DELAY, TIMER_DELAY, this::timerCallback);
|
||||
timerThread.executeWithFixedDelay(interval, interval, this::timerCallback);
|
||||
timerThread.executeWithFixedRate(0, 5000, this::clearSentBit);
|
||||
}
|
||||
|
||||
@@ -65,55 +79,63 @@ public class ClientOutboundDataService extends Service {
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleUpdateStackIntent(UpdateStackIntent sci) {
|
||||
stack.set(sci.getStack());
|
||||
private void handleStackCreatedIntent(StackCreatedIntent sci) {
|
||||
activeStacks.add(sci.getStack());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleDataPacketInboundIntent(DataPacketInboundIntent dpii) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
if (stack == null)
|
||||
return;
|
||||
PacketType type = PacketType.fromCrc(ByteBuffer.wrap(dpii.getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(2));
|
||||
switch (type) {
|
||||
case LAG_REQUEST:
|
||||
tuner.markLag();
|
||||
break;
|
||||
}
|
||||
private void handleStackDestroyedIntent(StackDestroyedIntent sdi) {
|
||||
activeStacks.remove(sdi.getStack());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleDataPacketOutboundIntent(DataPacketOutboundIntent dpoi) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
if (stack == null)
|
||||
return;
|
||||
PacketType type = PacketType.fromCrc(ByteBuffer.wrap(dpoi.getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(2));
|
||||
ClientServer filterServer = ClientServer.ZONE;
|
||||
switch (type) {
|
||||
case CMD_START_SCENE:
|
||||
tuner.markStart();
|
||||
case ERROR_MESSAGE:
|
||||
case SERVER_ID:
|
||||
case SERVER_NOW_EPOCH_TIME:
|
||||
filterServer = null;
|
||||
break;
|
||||
case CMD_SCENE_READY:
|
||||
tuner.markEnd();
|
||||
case LOGIN_CLUSTER_STATUS:
|
||||
case LOGIN_CLIENT_TOKEN:
|
||||
case LOGIN_INCORRECT_CLIENT_ID:
|
||||
case LOGIN_ENUM_CLUSTER:
|
||||
case ENUMERATE_CHARACTER_ID:
|
||||
case CHARACTER_CREATION_DISABLED:
|
||||
filterServer = ClientServer.LOGIN;
|
||||
break;
|
||||
case HEART_BEAT_MESSAGE: {
|
||||
HeartBeat heartbeat = new HeartBeat();
|
||||
heartbeat.decode(NetBuffer.wrap(dpoi.getData()));
|
||||
if (heartbeat.getPayload().length > 0) {
|
||||
stack.sendPing(heartbeat.getPayload());
|
||||
for (ProtocolStack stack : activeStacks)
|
||||
stack.sendPing(heartbeat.getPayload());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
synchronized (outboundMutex) {
|
||||
stack.addOutbound(dpoi.getData());
|
||||
if (filterServer == null) {
|
||||
Log.d("Sending %d bytes to %s", dpoi.getData().length, activeStacks);
|
||||
for (ProtocolStack stack : activeStacks)
|
||||
stack.addOutbound(dpoi.getData());
|
||||
} else {
|
||||
final ClientServer finalFilterServer = filterServer;
|
||||
ProtocolStack stack = activeStacks.stream().filter(s -> s.getServer() == finalFilterServer).findFirst().orElse(null);
|
||||
if (stack != null) {
|
||||
Log.d("Sending %d bytes to %s", dpoi.getData().length, stack);
|
||||
stack.addOutbound(dpoi.getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Multiplexer
|
||||
private void handleAcknowledgement(ProtocolStack stack, Acknowledge ack) {
|
||||
Log.t("Acknowledged: %d. Min Sequence: %d", ack.getSequence(), stack.getFirstUnacknowledgedOutbound());
|
||||
tuner.markAck();
|
||||
synchronized (outboundMutex) {
|
||||
stack.clearAcknowledgedOutbound(ack.getSequence());
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
@@ -124,7 +146,6 @@ public class ClientOutboundDataService extends Service {
|
||||
|
||||
@Multiplexer
|
||||
private void handleOutOfOrder(ProtocolStack stack, OutOfOrder ooo) {
|
||||
tuner.markOOO();
|
||||
synchronized (outboundMutex) {
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
if (outbound.getSequence() > ooo.getSequence())
|
||||
@@ -135,38 +156,38 @@ public class ClientOutboundDataService extends Service {
|
||||
}
|
||||
|
||||
private void clearSentBit() {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
if (stack == null)
|
||||
return;
|
||||
synchronized (outboundMutex) {
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
outbound.setSent(false);
|
||||
for (ProtocolStack stack : activeStacks) {
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
outbound.setSent(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void timerCallback() {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
if (stack == null)
|
||||
return;
|
||||
int maxSend = tuner.getMaxSend();
|
||||
int maxSend = data.getOutboundTunerMaxSend();
|
||||
if (maxSend <= 0)
|
||||
maxSend = 100;
|
||||
synchronized (outboundMutex) {
|
||||
stack.fillOutboundPackagedBuffer(maxSend);
|
||||
int sent = 0;
|
||||
int runStart = Integer.MIN_VALUE;
|
||||
int runEnd = 0;
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
runEnd = outbound.getSequence();
|
||||
if (runStart == Integer.MIN_VALUE) {
|
||||
runStart = runEnd;
|
||||
for (ProtocolStack stack : activeStacks) {
|
||||
stack.fillOutboundPackagedBuffer(maxSend);
|
||||
int sent = 0;
|
||||
int runStart = Integer.MIN_VALUE;
|
||||
int runEnd = 0;
|
||||
for (SequencedOutbound outbound : stack.getOutboundPackagedBuffer()) {
|
||||
runEnd = outbound.getSequence();
|
||||
if (runStart == Integer.MIN_VALUE) {
|
||||
runStart = runEnd;
|
||||
}
|
||||
stack.send(outbound.getData());
|
||||
Delay.sleepMicro(50);
|
||||
if (sent++ >= maxSend)
|
||||
break;
|
||||
}
|
||||
stack.send(outbound.getData());
|
||||
Delay.sleepMicro(50);
|
||||
if (sent++ >= maxSend)
|
||||
break;
|
||||
if (runStart != Integer.MIN_VALUE)
|
||||
Log.t("Sending to %s: %d - %d", stack.getSource(), runStart, runEnd);
|
||||
}
|
||||
if (runStart != Integer.MIN_VALUE)
|
||||
Log.t("Sending to %s: %d - %d", stack.getSource(), runStart, runEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.projectswg.forwarder.services.client;
|
||||
|
||||
import com.projectswg.forwarder.intents.client.SonyPacketInboundIntent;
|
||||
import com.projectswg.forwarder.intents.client.UpdateStackIntent;
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import com.projectswg.forwarder.resources.networking.packets.ClientNetworkStatusUpdate;
|
||||
import com.projectswg.forwarder.resources.networking.packets.KeepAlive;
|
||||
@@ -12,30 +11,19 @@ import me.joshlarson.jlcommon.control.IntentMultiplexer;
|
||||
import me.joshlarson.jlcommon.control.IntentMultiplexer.Multiplexer;
|
||||
import me.joshlarson.jlcommon.control.Service;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ClientProtocolService extends Service {
|
||||
|
||||
private static final int GALACTIC_BASE_TIME = 1323043200;
|
||||
|
||||
private final AtomicReference<ProtocolStack> stack;
|
||||
private final IntentMultiplexer multiplexer;
|
||||
|
||||
public ClientProtocolService() {
|
||||
this.stack = new AtomicReference<>(null);
|
||||
this.multiplexer = new IntentMultiplexer(this, ProtocolStack.class, Packet.class);
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleSonyPacketInboundIntent(SonyPacketInboundIntent spii) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
assert stack != null : "stack is null";
|
||||
multiplexer.call(stack, spii.getPacket());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleUpdateStackIntent(UpdateStackIntent sci) {
|
||||
stack.set(sci.getStack());
|
||||
multiplexer.call(spii.getStack(), spii.getPacket());
|
||||
}
|
||||
|
||||
@Multiplexer
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package com.projectswg.forwarder.services.client;
|
||||
|
||||
import com.projectswg.forwarder.Forwarder.ForwarderData;
|
||||
import com.projectswg.forwarder.intents.client.ClientConnectedIntent;
|
||||
import com.projectswg.forwarder.intents.client.ClientDisconnectedIntent;
|
||||
import com.projectswg.forwarder.intents.client.SonyPacketInboundIntent;
|
||||
import com.projectswg.forwarder.intents.client.UpdateStackIntent;
|
||||
import com.projectswg.forwarder.intents.client.*;
|
||||
import com.projectswg.forwarder.intents.control.StartForwarderIntent;
|
||||
import com.projectswg.forwarder.intents.control.StopForwarderIntent;
|
||||
import com.projectswg.forwarder.intents.server.ServerDisconnectedIntent;
|
||||
import com.projectswg.forwarder.resources.networking.ClientServer;
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import com.projectswg.forwarder.resources.networking.packets.*;
|
||||
import com.projectswg.forwarder.resources.networking.packets.Disconnect.DisconnectReason;
|
||||
import me.joshlarson.jlcommon.control.IntentChain;
|
||||
import me.joshlarson.jlcommon.control.IntentHandler;
|
||||
import me.joshlarson.jlcommon.control.Service;
|
||||
@@ -19,12 +18,13 @@ import me.joshlarson.jlcommon.utilities.ByteUtilities;
|
||||
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClientServerService extends Service {
|
||||
|
||||
private final IntentChain intentChain;
|
||||
private final AtomicReference<ProtocolStack> stack;
|
||||
private final Map<ClientServer, ProtocolStack> stacks;
|
||||
|
||||
private ForwarderData data;
|
||||
private UDPServer loginServer;
|
||||
@@ -32,7 +32,7 @@ public class ClientServerService extends Service {
|
||||
|
||||
public ClientServerService() {
|
||||
this.intentChain = new IntentChain();
|
||||
this.stack = new AtomicReference<>(null);
|
||||
this.stacks = new EnumMap<>(ClientServer.class);
|
||||
this.data = null;
|
||||
this.loginServer = null;
|
||||
this.zoneServer = null;
|
||||
@@ -45,7 +45,8 @@ public class ClientServerService extends Service {
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
setStack(null);
|
||||
closeConnection(ClientServer.LOGIN);
|
||||
closeConnection(ClientServer.ZONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,6 +93,12 @@ public class ClientServerService extends Service {
|
||||
Log.i("Closed the login and zone udp servers");
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleServerDisconnectedIntent(ServerDisconnectedIntent sdi) {
|
||||
closeConnection(ClientServer.LOGIN);
|
||||
closeConnection(ClientServer.ZONE);
|
||||
}
|
||||
|
||||
private void customizeUdpServer(DatagramSocket socket) {
|
||||
try {
|
||||
socket.setReuseAddress(false);
|
||||
@@ -137,37 +144,73 @@ public class ClientServerService extends Service {
|
||||
}
|
||||
|
||||
private void broadcast(InetSocketAddress source, ClientServer server, Packet parsed) {
|
||||
ProtocolStack stack = this.stack.get();
|
||||
if (parsed instanceof SessionRequest) {
|
||||
if (stack != null && stack.getServer() == ClientServer.ZONE)
|
||||
return;
|
||||
stack = new ProtocolStack(source, server, (remote, data) -> send(remote, server, data));
|
||||
stack.setConnectionId(((SessionRequest) parsed).getConnectionId());
|
||||
setStack(stack);
|
||||
stack.send(new SessionResponse(((SessionRequest) parsed).getConnectionId(), 0, (byte) 0, (byte) 0, (byte) 0, 496));
|
||||
}
|
||||
if (stack != null && parsed instanceof PingPacket) {
|
||||
stack.setPingSource(source);
|
||||
} else if (stack == null || !stack.getSource().equals(source) || stack.getServer() != server) {
|
||||
ProtocolStack stack = process(source, server, parsed);
|
||||
if (stack == null) {
|
||||
Log.t("[%s]@%s DROPPED %s", source, server, parsed);
|
||||
return;
|
||||
}
|
||||
Log.t("[%s]@%s sent: %s", source, server, parsed);
|
||||
intentChain.broadcastAfter(getIntentManager(), new SonyPacketInboundIntent(parsed));
|
||||
if (parsed instanceof Disconnect) {
|
||||
Log.d("Received client disconnect with id %d and reason %s", ((Disconnect) parsed).getConnectionId(), ((Disconnect) parsed).getReason());
|
||||
setStack(null);
|
||||
}
|
||||
intentChain.broadcastAfter(getIntentManager(), new SonyPacketInboundIntent(stack, parsed));
|
||||
}
|
||||
|
||||
private void setStack(ProtocolStack stack) {
|
||||
Log.d("Updating stack: %s", stack);
|
||||
ProtocolStack oldStack = this.stack.getAndSet(stack);
|
||||
if (oldStack != null && stack == null)
|
||||
intentChain.broadcastAfter(getIntentManager(), new ClientDisconnectedIntent());
|
||||
if (stack != null && stack.getServer() == ClientServer.LOGIN)
|
||||
private ProtocolStack process(InetSocketAddress source, ClientServer server, Packet parsed) {
|
||||
if (parsed instanceof SessionRequest)
|
||||
return onSessionRequest(source, server, (SessionRequest) parsed);
|
||||
if (parsed instanceof Disconnect)
|
||||
return onDisconnect(source, server, (Disconnect) parsed);
|
||||
|
||||
ProtocolStack stack = stacks.get(server);
|
||||
if (stack == null)
|
||||
return null;
|
||||
|
||||
if (parsed instanceof PingPacket) {
|
||||
stack.setPingSource(source);
|
||||
} else if (!stack.getSource().equals(source) || stack.getServer() != server) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private ProtocolStack onSessionRequest(InetSocketAddress source, ClientServer server, SessionRequest request) {
|
||||
ProtocolStack stack = new ProtocolStack(source, server, (remote, data) -> send(remote, server, data));
|
||||
stack.setConnectionId(request.getConnectionId());
|
||||
|
||||
openConnection(server, stack);
|
||||
return stack;
|
||||
}
|
||||
|
||||
private ProtocolStack onDisconnect(InetSocketAddress source, ClientServer server, Disconnect disconnect) {
|
||||
// Sets the current stack to null if it matches the Disconnect packet
|
||||
ProtocolStack stack = stacks.get(server);
|
||||
if (stack != null && stack.getSource().equals(source) && stack.getConnectionId() == disconnect.getConnectionId()) {
|
||||
closeConnection(server);
|
||||
} else {
|
||||
send(source, server, new Disconnect(disconnect.getConnectionId(), DisconnectReason.APPLICATION).encode().array());
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
private void openConnection(ClientServer server, ProtocolStack stack) {
|
||||
closeConnection(server);
|
||||
if (server == ClientServer.LOGIN) {
|
||||
closeConnection(ClientServer.ZONE);
|
||||
intentChain.broadcastAfter(getIntentManager(), new ClientConnectedIntent());
|
||||
intentChain.broadcastAfter(getIntentManager(), new UpdateStackIntent(stack));
|
||||
}
|
||||
stacks.put(server, stack);
|
||||
|
||||
stack.send(new SessionResponse(stack.getConnectionId(), 0, (byte) 0, (byte) 0, (byte) 0, 496));
|
||||
intentChain.broadcastAfter(getIntentManager(), new StackCreatedIntent(stack));
|
||||
}
|
||||
|
||||
private void closeConnection(ClientServer server) {
|
||||
ProtocolStack stack = stacks.remove(server);
|
||||
if (stack == null)
|
||||
return;
|
||||
stack.send(new Disconnect(stack.getConnectionId(), DisconnectReason.APPLICATION));
|
||||
intentChain.broadcastAfter(getIntentManager(), new StackDestroyedIntent(stack));
|
||||
if (stacks.isEmpty())
|
||||
intentChain.broadcastAfter(getIntentManager(), new ClientDisconnectedIntent());
|
||||
}
|
||||
|
||||
private static Packet parse(byte [] rawData) {
|
||||
|
||||
@@ -3,13 +3,11 @@ package com.projectswg.forwarder.services.crash;
|
||||
import com.projectswg.forwarder.Forwarder.ForwarderData;
|
||||
import com.projectswg.forwarder.intents.client.ClientConnectedIntent;
|
||||
import com.projectswg.forwarder.intents.client.ClientDisconnectedIntent;
|
||||
import com.projectswg.forwarder.intents.client.UpdateStackIntent;
|
||||
import com.projectswg.forwarder.intents.control.ClientCrashedIntent;
|
||||
import com.projectswg.forwarder.intents.control.StartForwarderIntent;
|
||||
import com.projectswg.forwarder.intents.control.StopForwarderIntent;
|
||||
import com.projectswg.forwarder.intents.server.ServerConnectedIntent;
|
||||
import com.projectswg.forwarder.intents.server.ServerDisconnectedIntent;
|
||||
import com.projectswg.forwarder.resources.networking.data.ProtocolStack;
|
||||
import me.joshlarson.jlcommon.control.Intent;
|
||||
import me.joshlarson.jlcommon.control.IntentHandler;
|
||||
import me.joshlarson.jlcommon.control.Service;
|
||||
@@ -80,15 +78,6 @@ public class IntentRecordingService extends Service {
|
||||
log(cdi, "");
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleUpdateStackIntent(UpdateStackIntent usi) {
|
||||
ProtocolStack stack = usi.getStack();
|
||||
if (stack == null)
|
||||
log(usi, "Stack='null'");
|
||||
else
|
||||
log(usi, "Server='%s' Source='%s' ConnectionId='%d'", stack.getServer(), stack.getSource(), stack.getConnectionId());
|
||||
}
|
||||
|
||||
@IntentHandler
|
||||
private void handleStartForwarderIntent(StartForwarderIntent sfi) {
|
||||
this.data = sfi.getData();
|
||||
|
||||
Reference in New Issue
Block a user