Changed TCP connection server to an HTTP server

This commit is contained in:
Josh Larson
2017-03-02 14:13:56 -06:00
parent 3f63324328
commit fedc69b864
49 changed files with 338 additions and 885 deletions

View File

@@ -6,5 +6,6 @@
<classpathentry kind="lib" path="lib/sqlite-jdbc-3.8.11.2.jar"/>
<classpathentry kind="lib" path="lib/fast-json-1.4.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="lib" path="/home/josh/Downloads/nanohttpd-nanohttpd-project-2.3.1/core/target/nanohttpd-2.3.1.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -1,7 +1,6 @@
CREATE TABLE IF NOT EXISTS servers (
id TEXT PRIMARY KEY,
directory TEXT,
origin TEXT,
jvm_arguments TEXT
);

View File

@@ -44,6 +44,11 @@ public class Lightspeed {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
running.set(false);
mainThread.interrupt();
try {
mainThread.join();
} catch (Exception e) {
Log.e(e);
}
}, "lightspeed-shutdown-hook"));
runLightspeed(lightspeed, running);
LightspeedManager.getDataManager().terminate();

View File

@@ -27,6 +27,8 @@
***********************************************************************************/
package com.projectswg.common.control;
import com.projectswg.common.info.Log;
public class Assert {
private static volatile AssertLevel level = AssertLevel.ASSERT;
@@ -40,25 +42,46 @@ public class Assert {
}
public static void notNull(Object o) {
notNull(o, "");
}
public static void notNull(Object o, String message) {
if (debug() && o == null)
handle(new NullPointerException());
handle(new NullPointerException(message));
}
public static void isNull(Object o) {
isNull(o, "");
}
public static void isNull(Object o, String message) {
if (debug() && o != null)
handle(new AssertionException(message));
}
public static void test(boolean expr) {
test(expr, "");
}
public static void test(boolean expr, String message) {
if (debug() && !expr)
handle(new AssertionError());
handle(new AssertionException(message));
}
public static void fail() {
fail("");
}
public static void fail(String message) {
if (debug())
handle(new AssertionError());
handle(new AssertionException(message));
}
private static void handle(RuntimeException e) {
AssertLevel level = Assert.level;
switch (level) {
case WARN:
e.printStackTrace();
warn(e);
break;
case ASSERT:
throw e;
@@ -67,17 +90,22 @@ public class Assert {
}
}
private static void handle(Error e) {
AssertLevel level = Assert.level;
switch (level) {
case WARN:
e.printStackTrace();
break;
case ASSERT:
throw e;
default:
break;
private static void warn(Exception e) {
StackTraceElement [] elements = e.getStackTrace();
if (elements.length <= 1)
Log.e(e);
else
Log.e(e);
}
private static class AssertionException extends RuntimeException {
private static final long serialVersionUID = 1L;
public AssertionException(String message) {
super(message);
}
}
public enum AssertLevel {

View File

@@ -29,14 +29,12 @@ package com.projectswg.common.control;
public abstract class Intent {
private final String type;
private boolean broadcasted;
private boolean complete;
private Intent parallel;
private Intent sequential;
protected Intent(String type) {
this.type = type;
protected Intent() {
this.broadcasted = false;
this.complete = false;
this.parallel = null;
@@ -54,14 +52,6 @@ public abstract class Intent {
parallel = null;
}
/**
* Returns the type of intent
* @return the type of intent
*/
public synchronized String getType() {
return type;
}
/**
* Determines whether or not the intent has been broadcasted and processed
* by the system
@@ -130,7 +120,7 @@ public abstract class Intent {
@Override
public synchronized String toString() {
return getType();
return getClass().getSimpleName();
}
private synchronized void setAsParallel(Intent i) {

View File

@@ -45,14 +45,14 @@ public class IntentManager {
private static final IntentManager INSTANCE = new IntentManager();
private final Runnable broadcastRunnable;
private final Map <String, List<IntentReceiver>> intentRegistrations;
private final Map <Class<? extends Intent>, List<IntentReceiver>> intentRegistrations;
private final Queue <Intent> intentQueue;
private ExecutorService broadcastThreads;
private boolean initialized = false;
private boolean terminated = false;
private IntentManager() {
intentRegistrations = new HashMap<String, List<IntentReceiver>>();
intentRegistrations = new HashMap<>();
intentQueue = new IntentQueue<>();
initialize();
broadcastRunnable = () -> {
@@ -67,9 +67,7 @@ public class IntentManager {
public void initialize() {
if (!initialized) {
intentRegistrations.clear();
intentQueue.clear();
final int broadcastThreadCount = 4;
final int broadcastThreadCount = Runtime.getRuntime().availableProcessors() * 10;
broadcastThreads = Executors.newFixedThreadPool(broadcastThreadCount, ThreadUtilities.newThreadFactory("intent-processor-%d"));
initialized = true;
terminated = false;
@@ -94,14 +92,14 @@ public class IntentManager {
catch (RejectedExecutionException e) { } // This error is thrown when the server is being shut down
}
public void registerForIntent(String intentType, IntentReceiver r) {
public void registerForIntent(Class<? extends Intent> c, IntentReceiver r) {
if (r == null)
throw new NullPointerException("Cannot register a null value for an intent");
synchronized (intentRegistrations) {
List <IntentReceiver> intents = intentRegistrations.get(intentType);
List <IntentReceiver> intents = intentRegistrations.get(c);
if (intents == null) {
intents = new CopyOnWriteArrayList<>();
intentRegistrations.put(intentType, intents);
intentRegistrations.put(c, intents);
}
synchronized (intents) {
intents.add(r);
@@ -109,27 +107,10 @@ public class IntentManager {
}
}
public void unregisterForIntent(String intentType, IntentReceiver r) {
if (r == null)
return;
synchronized (intentRegistrations) {
if (!intentRegistrations.containsKey(intentType))
return;
List<IntentReceiver> receivers = intentRegistrations.get(intentType);
for (IntentReceiver recv : receivers) {
if (r == recv || r.equals(recv)) {
r = recv;
break;
}
}
receivers.remove(r);
}
}
private void broadcast(Intent i) {
List <IntentReceiver> receivers;
synchronized (intentRegistrations) {
receivers = intentRegistrations.get(i.getType());
receivers = intentRegistrations.get(i.getClass());
}
if (receivers == null) {
i.markAsComplete();
@@ -144,9 +125,9 @@ public class IntentManager {
private void broadcast(IntentReceiver r, Intent i) {
try {
r.onIntentReceived(i);
} catch (Exception e) {
} catch (Throwable t) {
Log.e("Fatal Exception while processing intent: " + i);
Log.e(e);
Log.e(t);
}
}

View File

@@ -27,16 +27,17 @@
***********************************************************************************/
package com.projectswg.common.control;
import java.util.function.Consumer;
import com.projectswg.common.data.BackendData;
import com.projectswg.common.info.Config;
import com.projectswg.common.info.ConfigFile;
import com.projectswg.common.info.DataManager;
import com.projectswg.common.info.Log;
/**
* A Service is a class that does a specific job for the application
*/
public abstract class Service implements IntentReceiver {
public abstract class Service {
private static DataManager dataManager = null;
@@ -90,27 +91,9 @@ public abstract class Service implements IntentReceiver {
return true;
}
/**
* Registers for the specified intent string
* @param type the intent string
*/
public void registerForIntent(String type) {
IntentManager.getInstance().registerForIntent(type, this);
}
/**
* Unregisters for the specified intent string
* @param type the intent string
*/
public void unregisterForIntent(String type) {
IntentManager.getInstance().unregisterForIntent(type, this);
}
/**
* Callback when an intent is received from the system
*/
public void onIntentReceived(Intent i) {
Log.w("Warning: " + getClass().getSimpleName() + " did not override onIntentReceived");
@SuppressWarnings("unchecked")
protected final <T extends Intent> void registerForIntent(Class<T> c, Consumer<T> consumer) {
IntentManager.getInstance().registerForIntent(c, (i) -> consumer.accept((T) i));
}
/**

View File

@@ -32,8 +32,6 @@ import com.projectswg.lightspeed.build.ServerBuildData;
public class LaunchStarshipIntent extends Intent {
public static final String TYPE = "LaunchStarshipIntent";
private ServerBuildData build;
public LaunchStarshipIntent() {
@@ -41,7 +39,6 @@ public class LaunchStarshipIntent extends Intent {
}
public LaunchStarshipIntent(ServerBuildData build) {
super(TYPE);
setBuild(build);
}

View File

@@ -1,68 +0,0 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore 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. *
* *
* Holocore 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 Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.common.intents;
import java.net.InetSocketAddress;
import com.projectswg.common.control.Intent;
import com.projectswg.common.network.packets.Packet;
public class OutboundPacketIntent extends Intent {
public static final String TYPE = "OutboundPacketIntent";
private InetSocketAddress address;
private Packet packet;
public OutboundPacketIntent() {
this(null, null);
}
public OutboundPacketIntent(InetSocketAddress address, Packet packet) {
super(TYPE);
setAddress(address);
setPacket(packet);
}
public InetSocketAddress getAddress() {
return address;
}
public Packet getPacket() {
return packet;
}
public void setAddress(InetSocketAddress address) {
this.address = address;
}
public void setPacket(Packet packet) {
this.packet = packet;
}
}

View File

@@ -32,8 +32,6 @@ import com.projectswg.lightspeed.server.ServerServerData;
public class PrepareStarshipIntent extends Intent {
public static final String TYPE = "PrepareStarshipIntent";
private ServerServerData server;
public PrepareStarshipIntent() {
@@ -41,7 +39,6 @@ public class PrepareStarshipIntent extends Intent {
}
public PrepareStarshipIntent(ServerServerData server) {
super(TYPE);
setServer(server);
}

View File

@@ -27,42 +27,34 @@
***********************************************************************************/
package com.projectswg.common.intents;
import java.net.InetSocketAddress;
import com.projectswg.common.control.Intent;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.PacketType;
import com.projectswg.lightspeed.communication.HttpResponder;
public class InboundPacketIntent extends Intent {
public class RegisterHttpListenerIntent extends Intent {
public static final String TYPE = "InboundPacketIntent";
private PacketType requestType;
private HttpResponder predicate;
private InetSocketAddress address;
private Packet packet;
public InboundPacketIntent() {
this(null, null);
public RegisterHttpListenerIntent(PacketType requestType, HttpResponder responder) {
setRequestType(requestType);
setHttpResponder(responder);
}
public InboundPacketIntent(InetSocketAddress address, Packet packet) {
super(TYPE);
setAddress(address);
setPacket(packet);
public PacketType getRequestType() {
return requestType;
}
public InetSocketAddress getAddress() {
return address;
public HttpResponder getHttpResponder() {
return predicate;
}
public Packet getPacket() {
return packet;
public void setRequestType(PacketType requestType) {
this.requestType = requestType;
}
public void setAddress(InetSocketAddress address) {
this.address = address;
}
public void setPacket(Packet packet) {
this.packet = packet;
public void setHttpResponder(HttpResponder predicate) {
this.predicate = predicate;
}
}

View File

@@ -28,7 +28,6 @@
package com.projectswg.common.network.packets;
import com.projectswg.common.info.Log;
import com.projectswg.common.network.packets.init.*;
import com.projectswg.common.network.packets.post.*;
import com.projectswg.common.network.packets.request.*;
import com.projectswg.common.network.packets.response.*;
@@ -73,14 +72,13 @@ public class Packet {
public static Packet inflate(JSONObject json, PacketType type) {
switch (type) {
case INIT_PROTOCOL_VERSION: return new InitProtocolVersionPacket(json);
case CONNECTED: return new ConnectedPacket(json);
case POST_BUILD: return new PostBuildPacket(json);
case POST_DEPLOY: return new PostDeployPacket(json);
case POST_STOP_BUILD: return new PostStopBuildPacket(json);
case POST_STOP_DEPLOY: return new PostStopDeployPacket(json);
case POST_CREATE_SERVER: return new PostCreateServerPacket(json);
case POST_DELETE_SERVER: return new PostDeleteServerPacket(json);
case POST_RESULT: return new PostResult(json);
case REQUEST_SERVER_LIST: return new RequestServerListPacket(json);
case REQUEST_BUILD_LIST: return new RequestBuildListPacket(json);
case REQUEST_DEPLOYMENT_LIST: return new RequestDeploymentListPacket(json);

View File

@@ -29,10 +29,6 @@ package com.projectswg.common.network.packets;
public enum PacketType {
// INIT
INIT_PROTOCOL_VERSION,
CONNECTED,
// GET-REQUEST
REQUEST_SERVER_LIST,
REQUEST_BUILD_LIST,
@@ -50,6 +46,7 @@ public enum PacketType {
RESPONSE_DEPLOYMENT_DETAILED,
// POST
POST_RESULT,
POST_BUILD,
POST_DEPLOY,
POST_STOP_BUILD,

View File

@@ -1,56 +0,0 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore 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. *
* *
* Holocore 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 Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.common.network.packets.init;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.PacketType;
public class InitProtocolVersionPacket extends Packet {
private static final String VERSION = "version";
public InitProtocolVersionPacket(JSONObject json) {
super(json, PacketType.INIT_PROTOCOL_VERSION);
}
public InitProtocolVersionPacket(String version) {
super(PacketType.INIT_PROTOCOL_VERSION);
getJSON().put(VERSION, version);
}
public String getVersion() {
return getJSON().getString(VERSION);
}
public void setVersion(String version) {
getJSON().put(VERSION, version);
}
}

View File

@@ -37,11 +37,11 @@ public class PostDeployPacket extends Packet {
private static final String SERVER_ID = "server_id";
public PostDeployPacket(JSONObject json) {
super(json, PacketType.POST_DELETE_SERVER);
super(json, PacketType.POST_DEPLOY);
}
public PostDeployPacket(String serverId) {
super(PacketType.POST_DELETE_SERVER);
super(PacketType.POST_DEPLOY);
setServerId(serverId);
}

View File

@@ -25,21 +25,32 @@
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.common.network.packets.init;
package com.projectswg.common.network.packets.post;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.PacketType;
public class ConnectedPacket extends Packet {
public class PostResult extends Packet {
public ConnectedPacket() {
super(PacketType.CONNECTED);
private static final String SUCCESS = "success";
public PostResult(JSONObject json) {
super(json, PacketType.POST_RESULT);
}
public ConnectedPacket(JSONObject json) {
super(json, PacketType.CONNECTED);
public PostResult(boolean success) {
super(PacketType.POST_RESULT);
setSuccess(success);
}
public boolean isSuccess() {
return getJSON().getBoolean(SUCCESS);
}
public void setSuccess(boolean success) {
getJSON().put(SUCCESS, success);
}
}

View File

@@ -40,7 +40,7 @@ public class RequestBuildDetailedPacket extends RequestPacket {
}
public RequestBuildDetailedPacket(long buildId) {
super(PacketType.REQUEST_BUILD_DETAILED, PacketType.RESPONSE_BUILD_DETAILED);
super(PacketType.REQUEST_BUILD_DETAILED);
setBuildId(buildId);
}

View File

@@ -40,7 +40,7 @@ public class RequestBuildListPacket extends RequestPacket {
}
public RequestBuildListPacket(String serverId) {
super(PacketType.REQUEST_BUILD_LIST, PacketType.RESPONSE_BUILD_LIST);
super(PacketType.REQUEST_BUILD_LIST);
setServerId(serverId);
}

View File

@@ -40,7 +40,7 @@ public class RequestDeploymentDetailedPacket extends RequestPacket {
}
public RequestDeploymentDetailedPacket(long deploymentId) {
super(PacketType.REQUEST_DEPLOYMENT_DETAILED, PacketType.RESPONSE_DEPLOYMENT_DETAILED);
super(PacketType.REQUEST_DEPLOYMENT_DETAILED);
setDeploymentId(deploymentId);
}

View File

@@ -40,7 +40,7 @@ public class RequestDeploymentListPacket extends RequestPacket {
}
public RequestDeploymentListPacket(String serverId) {
super(PacketType.REQUEST_DEPLOYMENT_LIST, PacketType.RESPONSE_DEPLOYMENT_LIST);
super(PacketType.REQUEST_DEPLOYMENT_LIST);
setServerId(serverId);
}

View File

@@ -32,35 +32,14 @@ import me.joshlarson.json.JSONObject;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.PacketType;
public class RequestPacket extends Packet {
private static final String REQUESTED_TYPE = "request_type";
private static final String REQUESTED_ID = "request_id";
public abstract class RequestPacket extends Packet {
public RequestPacket(JSONObject json, PacketType type) {
super(json, type);
}
public RequestPacket(PacketType type, PacketType requested) {
public RequestPacket(PacketType type) {
super(type);
setRequestedType(requested);
setRequestedId(-1);
}
public PacketType getRequestedPacket() {
return PacketType.valueOf(getJSON().getString(REQUESTED_TYPE));
}
public long getRequestId() {
return getJSON().getLong(REQUESTED_ID);
}
public void setRequestedType(PacketType type) {
getJSON().put(REQUESTED_TYPE, type.name());
}
public void setRequestedId(long requestId) {
getJSON().put(REQUESTED_ID, requestId);
}
}

View File

@@ -40,7 +40,7 @@ public class RequestServerDetailedPacket extends RequestPacket {
}
public RequestServerDetailedPacket(String serverId) {
super(PacketType.REQUEST_SERVER_DETAILED, PacketType.RESPONSE_SERVER_DETAILED);
super(PacketType.REQUEST_SERVER_DETAILED);
setServerId(serverId);
}

View File

@@ -38,7 +38,7 @@ public class RequestServerListPacket extends RequestPacket {
}
public RequestServerListPacket() {
super(PacketType.REQUEST_SERVER_LIST, PacketType.RESPONSE_SERVER_LIST);
super(PacketType.REQUEST_SERVER_LIST);
}
}

View File

@@ -42,7 +42,6 @@ public class ResponseBuildDetailedPacket extends ResponsePacket {
private static final String BUILD_STRING = "build_string";
private static final String TEST_STRING = "test_string";
private static final String COMPILE_TIME = "compile_time";
private static final String OFFLINE = "offline";
private static final String BUILD_SUCCESS = "build_success";
private static final String TEST_SUCCESS = "test_success";
private static final String CANCELLED = "cancelled";
@@ -82,10 +81,6 @@ public class ResponseBuildDetailedPacket extends ResponsePacket {
return getJSON().getDouble(COMPILE_TIME);
}
public boolean isOffline() {
return getJSON().getBoolean(OFFLINE);
}
public boolean isBuildSuccess() {
return getJSON().getBoolean(BUILD_SUCCESS);
}
@@ -112,7 +107,6 @@ public class ResponseBuildDetailedPacket extends ResponsePacket {
build.setBuildString(getBuildString());
build.setTestString(getTestString());
build.setCompileTime(getCompileTime());
build.setOffline(isOffline());
build.setBuildSuccess(isBuildSuccess());
build.setTestSuccess(isTestSuccess());
build.setCancelled(isCancelled());
@@ -144,10 +138,6 @@ public class ResponseBuildDetailedPacket extends ResponsePacket {
getJSON().put(COMPILE_TIME, compileTime);
}
public void setOffline(boolean offline) {
getJSON().put(OFFLINE, offline);
}
public void setBuildSuccess(boolean buildSuccess) {
getJSON().put(BUILD_SUCCESS, buildSuccess);
}
@@ -176,7 +166,6 @@ public class ResponseBuildDetailedPacket extends ResponsePacket {
setBuildString(buildData.getBuildString());
setTestString(buildData.getTestString());
setCompileTime(buildData.getCompileTime());
setOffline(buildData.isOffline());
setBuildSuccess(buildData.isBuildSuccess());
setTestSuccess(buildData.isTestSuccess());
setCancelled(buildData.isCancelled());

View File

@@ -32,26 +32,14 @@ import me.joshlarson.json.JSONObject;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.PacketType;
public class ResponsePacket extends Packet {
private static final String REQUESTED_ID = "request_id";
public abstract class ResponsePacket extends Packet {
public ResponsePacket(JSONObject json, PacketType type) {
super(json, type);
setRequestedId(json.getLong(REQUESTED_ID));
}
public ResponsePacket(PacketType type) {
super(type);
setRequestedId(-1);
}
public long getRequestId() {
return getJSON().getLong(REQUESTED_ID);
}
public void setRequestedId(long requestId) {
getJSON().put(REQUESTED_ID, requestId);
}
}

View File

@@ -28,41 +28,51 @@
package com.projectswg.lightspeed;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.control.Assert;
import com.projectswg.common.control.Intent;
import com.projectswg.common.control.Service;
import com.projectswg.common.info.Config;
import com.projectswg.common.info.ConfigFile;
import com.projectswg.common.info.Log;
import com.projectswg.common.intents.OutboundPacketIntent;
import com.projectswg.common.network.TCPServer;
import com.projectswg.common.network.TCPServer.TCPCallback;
import com.projectswg.lightspeed.communication.LightspeedSession;
import com.projectswg.common.intents.RegisterHttpListenerIntent;
import com.projectswg.common.network.packets.PacketType;
import com.projectswg.lightspeed.communication.HttpResponder;
public class CommunicationService extends Service implements TCPCallback {
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
import fi.iki.elonen.NanoHTTPD.Response;
import fi.iki.elonen.NanoHTTPD.Response.Status;
public class CommunicationService extends Service {
private final TCPServer server;
private final Map<InetSocketAddress, LightspeedSession> sessions;
private final NanoHTTPD httpServer;
private final Map<PacketType, HttpResponder> responders;
public CommunicationService() {
server = new TCPServer(getConfig(ConfigFile.LIGHTSPEED).getInt("PORT", 44444), 4096);
sessions = new HashMap<>();
server.setCallback(this);
httpServer = new NanoHTTPD(44444) {
public Response serve(IHTTPSession session) {
return CommunicationService.this.serve(session);
}
};
responders = new HashMap<>();
registerForIntent(OutboundPacketIntent.TYPE);
registerForIntent(RegisterHttpListenerIntent.class, rhli -> handleRegisterListener(rhli));
}
@Override
public boolean initialize() {
Config config = getConfig(ConfigFile.LIGHTSPEED);
try {
server.bind();
httpServer.start(config.getInt("HTTP-TIMEOUT", 2000));
} catch (IOException e) {
e.printStackTrace();
Log.a(e);
return false;
}
return super.initialize();
@@ -70,75 +80,93 @@ public class CommunicationService extends Service implements TCPCallback {
@Override
public boolean terminate() {
server.close();
httpServer.stop();
return super.terminate();
}
@Override
public void onIntentReceived(Intent i) {
if (i instanceof OutboundPacketIntent) {
LightspeedSession session;
synchronized (sessions) {
session = sessions.get(((OutboundPacketIntent) i).getAddress());
}
if (session == null) {
Log.e("Unknown session for address: %s", ((OutboundPacketIntent) i).getAddress());
return;
}
session.send(((OutboundPacketIntent) i).getPacket());
}
}
@Override
public void onIncomingConnection(Socket s) {
SocketAddress addr = s.getRemoteSocketAddress();
if (addr == null) {
Log.e("Unable to accept incoming connection! Address is null.");
private Response serve(IHTTPSession session) {
try {
JSONObject params = getJSONObject(session);
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
switch (session.getMethod()) {
case GET:
params.put("type", "REQUEST_" + params.getString("type"));
return process(params);
case POST:
params.put("type", "POST_" + params.getString("type"));
return process(params);
default:
return getMethodNotAllowed();
}
} catch (IllegalArgumentException e) {
Log.w(e.toString());
return getNotFound("Invalid "+session.getMethod()+" Type: " + params.getString("type"));
} catch (Exception e) {
Log.w(e);
return getServerError(e.toString() + "\nRequest:\n" + params);
}
return;
} catch (Throwable t) {
Log.w(t);
return getServerError(t.toString());
}
if (!(addr instanceof InetSocketAddress)) {
Log.e("Unable to accept incoming connection! Address is not InetSocketAddress.");
}
private Response process(JSONObject params) {
PacketType request = PacketType.valueOf(params.getString("type"));
HttpResponder responder = responders.get(request);
if (responder == null)
return getNotFound("No Services Ready to Respond to " + request.name()); // From PacketType.valueOf
JSONObject response = responder.request(request, params);
if (response == null)
return getNotFound("Invalid Request Parameters");
return NanoHTTPD.newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_PLAINTEXT, response.toString());
}
private JSONObject getJSONObject(IHTTPSession session) {
JSONObject ret = new JSONObject();
for (Entry<String, List<String>> param : session.getParameters().entrySet()) {
if (param.getValue().isEmpty())
continue;
String key = new String(param.getKey().getBytes(StandardCharsets.US_ASCII));
String val = new String(param.getValue().get(0).getBytes(StandardCharsets.US_ASCII));
if (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false")) {
ret.put(key, val.equalsIgnoreCase("true"));
continue;
}
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
Log.i("%s connected", addr);
synchronized (sessions) {
sessions.put((InetSocketAddress) addr, new LightspeedSession(server, (InetSocketAddress) addr));
long l = Long.parseLong(val);
ret.put(key, l);
continue;
} catch (NumberFormatException e) { }
try {
double d = Double.parseDouble(val);
ret.put(key, d);
continue;
} catch (NumberFormatException e) { }
ret.put(key, val);
}
return ret;
}
@Override
public void onConnectionDisconnect(Socket s, SocketAddress addr) {
Assert.notNull(addr);
Log.i("%s disconnected", addr);
synchronized (sessions) {
sessions.remove(addr);
}
private Response getMethodNotAllowed() {
return NanoHTTPD.newFixedLengthResponse(Status.METHOD_NOT_ALLOWED, NanoHTTPD.MIME_PLAINTEXT, "Method Not Allowed");
}
@Override
public void onIncomingData(Socket s, byte[] data) {
SocketAddress addr = s.getRemoteSocketAddress();
Assert.notNull(addr);
LightspeedSession session;
synchronized (sessions) {
session = sessions.get(addr);
}
if (session != null)
session.onIncoming(data);
private Response getNotFound(String str) {
return NanoHTTPD.newFixedLengthResponse(Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, str);
}
private Response getServerError(String str) {
return NanoHTTPD.newFixedLengthResponse(Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, str);
}
private void handleRegisterListener(RegisterHttpListenerIntent rhli) {
Log.i("Registering a responder for %s", rhli.getRequestType());
Assert.isNull(responders.put(rhli.getRequestType(), rhli.getHttpResponder()), "Cannot register two responders for the same packet type!");
}
public int getPort() {
return server.getPort();
return httpServer.getListeningPort();
}
}

View File

@@ -28,7 +28,6 @@
package com.projectswg.lightspeed;
import java.io.File;
import java.net.InetSocketAddress;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -39,19 +38,20 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.control.Assert;
import com.projectswg.common.control.Intent;
import com.projectswg.common.control.Service;
import com.projectswg.common.info.Config;
import com.projectswg.common.info.ConfigFile;
import com.projectswg.common.info.Log;
import com.projectswg.common.info.RelationalDatabase;
import com.projectswg.common.info.RelationalServerFactory;
import com.projectswg.common.intents.InboundPacketIntent;
import com.projectswg.common.intents.LaunchStarshipIntent;
import com.projectswg.common.intents.OutboundPacketIntent;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.intents.RegisterHttpListenerIntent;
import com.projectswg.common.network.packets.PacketType;
import com.projectswg.common.network.packets.post.PostDeployPacket;
import com.projectswg.common.network.packets.post.PostResult;
import com.projectswg.common.network.packets.post.PostStopDeployPacket;
import com.projectswg.common.network.packets.request.RequestDeploymentDetailedPacket;
import com.projectswg.common.network.packets.request.RequestDeploymentListPacket;
@@ -74,8 +74,7 @@ public class DeploymentService extends Service {
deployments = new HashMap<>();
deploymentHistory = new HashSet<>();
registerForIntent(LaunchStarshipIntent.TYPE);
registerForIntent(InboundPacketIntent.TYPE);
registerForIntent(LaunchStarshipIntent.class, lsi -> handleLaunchStarshipIntent(lsi));
}
@Override
@@ -89,6 +88,10 @@ public class DeploymentService extends Service {
} catch (SQLException e) {
e.printStackTrace();
}
new RegisterHttpListenerIntent(PacketType.POST_DEPLOY, (type, params) -> processPostDeploy(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.POST_STOP_DEPLOY, (type, params) -> processStopDeployment(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.REQUEST_DEPLOYMENT_LIST, (type, params) -> processDeploymentList(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.REQUEST_DEPLOYMENT_DETAILED, (type, params) -> processDeploymentDetailed(params)).broadcast();
return super.initialize();
}
@@ -96,23 +99,20 @@ public class DeploymentService extends Service {
public boolean terminate() {
synchronized (deployments) {
for (ServerDeploymentData deployment : deployments.values()) {
if (deployment.getProcess() != null)
if (deployment.getProcess() != null) {
Log.i("Shutting down child process: %s", deployment.getServer().getName());
deployment.getProcess().stop();
}
}
}
return super.terminate();
}
@Override
public void onIntentReceived(Intent i) {
if (i instanceof LaunchStarshipIntent) {
ServerBuildData build = ((LaunchStarshipIntent) i).getBuild();
Log.i("Deploying %s", build.getServer().getName());
ServerDeploymentData deployment = new ServerDeploymentData(deploymentId.incrementAndGet(), build);
launch(deployment);
} else if (i instanceof InboundPacketIntent) {
processInboundPacketIntent(((InboundPacketIntent) i).getAddress(), ((InboundPacketIntent) i).getPacket());
}
private void handleLaunchStarshipIntent(LaunchStarshipIntent lsi) {
ServerBuildData build = lsi.getBuild();
Log.i("Deploying %s", build.getServer().getName());
ServerDeploymentData deployment = new ServerDeploymentData(deploymentId.incrementAndGet(), build);
launch(deployment);
}
private File getJdk() {
@@ -130,47 +130,47 @@ public class DeploymentService extends Service {
return jdk;
}
private void processInboundPacketIntent(InetSocketAddress address, Packet p) {
switch (p.getType()) {
case POST_DEPLOY: launchStarship(((PostDeployPacket) p).getServerId()); break;
case POST_STOP_DEPLOY: processStopDeployment((PostStopDeployPacket) p); break;
case REQUEST_DEPLOYMENT_LIST: processDeploymentList(address, (RequestDeploymentListPacket) p); break;
case REQUEST_DEPLOYMENT_DETAILED: processDeploymentDetailed(address, (RequestDeploymentDetailedPacket) p); break;
default: break;
}
private JSONObject processPostDeploy(JSONObject params) {
PostDeployPacket deploy = new PostDeployPacket(params);
launchStarship(deploy.getServerId());
return new PostResult(true).getJSON();
}
private void processDeploymentDetailed(InetSocketAddress address, RequestDeploymentDetailedPacket p) {
private JSONObject processStopDeployment(JSONObject params) {
PostStopDeployPacket p = new PostStopDeployPacket(params);
synchronized (deployments) {
ServerDeploymentData d = deployments.get(p.getServerId());
if (d != null && d.getProcess() != null) {
d.getProcess().stop();
return new PostResult(true).getJSON();
}
}
return new PostResult(false).getJSON();
}
private JSONObject processDeploymentDetailed(JSONObject params) {
RequestDeploymentDetailedPacket request = new RequestDeploymentDetailedPacket(params);
synchronized (deploymentHistory) {
for (ServerDeploymentData d : deploymentHistory) {
if (d.getId() == p.getDeploymentId()) {
new OutboundPacketIntent(address, new ResponseDeploymentDetailedPacket(d.getShared())).broadcast();
break;
if (d.getId() == request.getDeploymentId()) {
return new ResponseDeploymentDetailedPacket(d.getShared()).getJSON();
}
}
}
return null;
}
private void processDeploymentList(InetSocketAddress address, RequestDeploymentListPacket p) {
private JSONObject processDeploymentList(JSONObject params) {
RequestDeploymentListPacket request = new RequestDeploymentListPacket(params);
List<SharedDeploymentData> deployList = new ArrayList<>();
synchronized (deploymentHistory) {
for (ServerDeploymentData d : deploymentHistory) {
if (d.getServer().getName().equals(p.getServerId())) {
if (d.getServer().getName().equals(request.getServerId())) {
deployList.add(d.getShared());
}
}
}
new OutboundPacketIntent(address, new ResponseDeploymentListPacket(p.getServerId(), deployList)).broadcast();
}
private void processStopDeployment(PostStopDeployPacket p) {
synchronized (deployments) {
ServerDeploymentData d = deployments.get(p.getServerId());
if (d != null) {
if (d.getProcess() != null)
d.getProcess().stop();
}
}
return new ResponseDeploymentListPacket(request.getServerId(), deployList).getJSON();
}
private void launchStarship(String serverId) {

View File

@@ -29,7 +29,6 @@ package com.projectswg.lightspeed.build;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.sql.ResultSet;
@@ -42,20 +41,21 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.control.Assert;
import com.projectswg.common.control.Intent;
import com.projectswg.common.control.Service;
import com.projectswg.common.info.Config;
import com.projectswg.common.info.ConfigFile;
import com.projectswg.common.info.Log;
import com.projectswg.common.info.RelationalDatabase;
import com.projectswg.common.info.RelationalServerFactory;
import com.projectswg.common.intents.InboundPacketIntent;
import com.projectswg.common.intents.LaunchStarshipIntent;
import com.projectswg.common.intents.OutboundPacketIntent;
import com.projectswg.common.intents.PrepareStarshipIntent;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.intents.RegisterHttpListenerIntent;
import com.projectswg.common.network.packets.PacketType;
import com.projectswg.common.network.packets.post.PostBuildPacket;
import com.projectswg.common.network.packets.post.PostResult;
import com.projectswg.common.network.packets.post.PostStopBuildPacket;
import com.projectswg.common.network.packets.request.RequestBuildDetailedPacket;
import com.projectswg.common.network.packets.request.RequestBuildListPacket;
@@ -78,8 +78,7 @@ public class BuildService extends Service {
inprogress = new HashMap<>();
buildHistory = new HashSet<>();
registerForIntent(PrepareStarshipIntent.TYPE);
registerForIntent(InboundPacketIntent.TYPE);
registerForIntent(PrepareStarshipIntent.class, psi -> onPrepareStarshipIntent(psi));
}
@Override
@@ -93,16 +92,13 @@ public class BuildService extends Service {
} catch (SQLException e) {
e.printStackTrace();
}
new RegisterHttpListenerIntent(PacketType.POST_BUILD, (type, params) -> processBuild(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.POST_STOP_BUILD, (type, params) -> processStopBuild(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.REQUEST_BUILD_LIST, (type, params) -> processBuildList(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.REQUEST_BUILD_DETAILED, (type, params) -> processBuildDetailed(params)).broadcast();
return super.initialize();
}
public void onIntentReceived(Intent i) {
if (i instanceof PrepareStarshipIntent)
onPrepareStarshipIntent((PrepareStarshipIntent) i);
else if (i instanceof InboundPacketIntent)
onInboundPacketIntent((InboundPacketIntent) i);
}
private File getJdk() {
Config config = getConfig(ConfigFile.LIGHTSPEED);
String path = config.getString("jdk", System.getProperty("java.home"));
@@ -135,53 +131,47 @@ public class BuildService extends Service {
}
}
private void onInboundPacketIntent(InboundPacketIntent ipi) {
Packet p = ipi.getPacket();
switch (p.getType()) {
case POST_BUILD: processBuild((PostBuildPacket) p); break;
case POST_STOP_BUILD: processPostStopBuild((PostStopBuildPacket) p); break;
case REQUEST_BUILD_LIST: processBuildList(ipi.getAddress(), (RequestBuildListPacket) p); break;
case REQUEST_BUILD_DETAILED:processBuildDetailed(ipi.getAddress(), (RequestBuildDetailedPacket) p); break;
default: break;
}
}
private void processBuild(PostBuildPacket p) {
private JSONObject processBuild(JSONObject params) {
PostBuildPacket p = new PostBuildPacket(params);
ServerServerData server = getBackendData().getServer(p.getServerId());
if (server != null)
new PrepareStarshipIntent(server).broadcast();
return new PostResult(server != null).getJSON();
}
private void processPostStopBuild(PostStopBuildPacket p) {
private JSONObject processStopBuild(JSONObject params) {
PostStopBuildPacket p = new PostStopBuildPacket(params);
synchronized (inprogress) {
ServerBuildData installation = inprogress.get(p.getServerId());
if (installation != null)
installation.setState(InstallationState.CANCELLED);
return new PostResult(installation != null).getJSON();
}
}
private void processBuildList(InetSocketAddress address, RequestBuildListPacket p) {
private JSONObject processBuildList(JSONObject params) {
RequestBuildListPacket p = new RequestBuildListPacket(params);
List<SharedBuildData> buildList = new ArrayList<>();
synchronized (buildHistory) {
List<SharedBuildData> buildList = new ArrayList<>();
for (ServerBuildData build : buildHistory) {
if (p.getServerId().equals(build.getServer().getName())) {
buildList.add(build.getShared());
}
}
new OutboundPacketIntent(address, new ResponseBuildListPacket(p.getServerId(), buildList)).broadcast();
}
return new ResponseBuildListPacket(p.getServerId(), buildList).getJSON();
}
private void processBuildDetailed(InetSocketAddress address, RequestBuildDetailedPacket p) {
private JSONObject processBuildDetailed(JSONObject params) {
RequestBuildDetailedPacket p = new RequestBuildDetailedPacket(params);
synchronized (buildHistory) {
for (ServerBuildData build : buildHistory) {
if (p.getBuildId() == build.getId()) {
new OutboundPacketIntent(address, new ResponseBuildDetailedPacket(build.getShared())).broadcast();
break;
return new ResponseBuildDetailedPacket(build.getShared()).getJSON();
}
}
}
return null;
}
private void onCompleted(String serverId, ServerBuildData build) {

View File

@@ -27,8 +27,12 @@
***********************************************************************************/
package com.projectswg.lightspeed.communication;
public enum LightspeedState {
DISCONNECTED,
CONNECTING,
CONNECTED
import com.projectswg.common.network.packets.PacketType;
import me.joshlarson.json.JSONObject;
public interface HttpResponder {
JSONObject request(PacketType request, JSONObject params);
}

View File

@@ -1,114 +0,0 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore 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. *
* *
* Holocore 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 Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.lightspeed.communication;
import java.net.InetSocketAddress;
import com.projectswg.common.control.IntentChain;
import com.projectswg.common.intents.InboundPacketIntent;
import com.projectswg.common.network.TCPServer;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.init.ConnectedPacket;
import com.projectswg.common.network.packets.init.InitProtocolVersionPacket;
import com.projectswg.common.network.protocol.Protocol;
import com.projectswg.common.network.protocol.ProtocolFactory;
public class LightspeedSession {
private final TCPServer server;
private final InetSocketAddress address;
private final IntentChain incomingChain;
private final Object stateMutex;
private Protocol protocol;
private LightspeedState state;
public LightspeedSession(TCPServer server, InetSocketAddress address) {
this.server = server;
this.address = address;
this.incomingChain = new IntentChain();
this.stateMutex = new Object();
this.protocol = ProtocolFactory.createProtocol(null);
this.state = LightspeedState.CONNECTING;
}
public InetSocketAddress getAddress() {
return address;
}
public LightspeedState getState() {
synchronized (stateMutex) {
return state;
}
}
public void disconnect() {
server.disconnect(address);
updateState(LightspeedState.DISCONNECTED);
}
public void onIncoming(byte [] data) {
protocol.onIncomingData(data);
while (protocol.getPacketCount() > 0) {
Packet p = protocol.getNextPacket();
if (p == null)
continue;
incomingChain.broadcastAfter(new InboundPacketIntent(address, p));
processPacket(p);
}
}
public void send(Packet p) {
if (getState() == LightspeedState.CONNECTED)
server.send(address, protocol.preparePacket(p));
}
private boolean processPacket(Packet p) {
if (p instanceof InitProtocolVersionPacket && getState() == LightspeedState.CONNECTING)
return handleProtocolVersion(((InitProtocolVersionPacket) p).getVersion());
return true;
}
private boolean handleProtocolVersion(String version) {
Protocol requestedProtocol = ProtocolFactory.createProtocol(version);
if (requestedProtocol == null) {
disconnect();
return false;
}
this.protocol = requestedProtocol;
updateState(LightspeedState.CONNECTED);
send(new ConnectedPacket());
return true;
}
private void updateState(LightspeedState state) {
synchronized (stateMutex) {
this.state = state;
}
}
}

View File

@@ -28,39 +28,56 @@
package com.projectswg.lightspeed.server;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.control.Intent;
import com.projectswg.common.control.Service;
import com.projectswg.common.intents.InboundPacketIntent;
import com.projectswg.common.intents.OutboundPacketIntent;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.request.RequestServerListPacket;
import com.projectswg.common.intents.RegisterHttpListenerIntent;
import com.projectswg.common.network.packets.PacketType;
import com.projectswg.common.network.packets.request.RequestServerDetailedPacket;
import com.projectswg.common.network.packets.response.ResponseServerDetailedPacket;
import com.projectswg.common.network.packets.response.ResponseServerListPacket;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
public class ServerService extends Service {
private final Set<ServerServerData> servers;
public ServerService() {
registerForIntent(InboundPacketIntent.TYPE);
this.servers = new HashSet<>();
}
public void onIntentReceived(Intent i) {
if (i instanceof InboundPacketIntent)
onInboundPacketIntent((InboundPacketIntent) i);
@Override
public boolean initialize() {
servers.addAll(getBackendData().getServerList());
new RegisterHttpListenerIntent(PacketType.REQUEST_SERVER_LIST, (type, params) -> onRequestServerList(params)).broadcast();
new RegisterHttpListenerIntent(PacketType.REQUEST_SERVER_DETAILED, (type, params) -> onRequestServerDetailed(params)).broadcast();
return super.initialize();
}
private void onInboundPacketIntent(InboundPacketIntent i) {
Packet p = i.getPacket();
if (p instanceof RequestServerListPacket) {
List<ServerServerData> servers = getBackendData().getServerList();
List<SharedServerData> serverShared = new ArrayList<>(servers.size());
for (ServerServerData server : servers)
serverShared.add(server.getShared());
ResponseServerListPacket response = new ResponseServerListPacket(serverShared);
response.setRequestedId(((RequestServerListPacket) p).getRequestId());
new OutboundPacketIntent(i.getAddress(), response).broadcast();
private JSONObject onRequestServerList(JSONObject params) {
synchronized (servers) {
List<SharedServerData> serverList = new ArrayList<>(servers.size());
for (ServerServerData server : servers) {
serverList.add(server.getShared());
}
return new ResponseServerListPacket(serverList).getJSON();
}
}
private JSONObject onRequestServerDetailed(JSONObject params) {
synchronized (servers) {
RequestServerDetailedPacket request = new RequestServerDetailedPacket(params);
for (ServerServerData server : servers) {
if (server.getName().equals(request.getServerId())) {
return new ResponseServerDetailedPacket(server.getShared()).getJSON();
}
}
}
return null;
}
}

View File

@@ -28,10 +28,6 @@
package com.projectswg.lightspeed_frontend;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import com.projectswg.common.control.Assert;
import com.projectswg.common.info.Log;
import com.projectswg.common.network.packets.Packet;
@@ -41,27 +37,17 @@ import com.projectswg.common.network.packets.request.RequestPacket;
import com.projectswg.common.network.packets.request.RequestServerListPacket;
import com.projectswg.common.network.packets.response.ResponsePacket;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
import com.projectswg.lightspeed_frontend.communication.FrontendLightspeedSession.LightspeedSessionCallback;
import com.projectswg.lightspeed_frontend.data.SharedData;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
import com.projectswg.lightspeed_frontend.intents.ConnectionStateUpdateIntent;
public class Frontend {
private final FrontendCommunication communication;
private final SharedData data;
private final Map<Long, ResponseCallback> requests;
private final AtomicLong requestId;
public Frontend() {
communication = new FrontendCommunication();
data = new SharedData();
requests = new HashMap<>();
requestId = new AtomicLong(0);
communication.setCallback(new LightspeedSessionCallback() {
public void onConnectionStateChanged(boolean connected) { Frontend.this.onConnectionStateChanged(connected); }
public void onIncomingPacket(Packet p) { Frontend.this.processIncoming(p); }
});
}
public void start() {
@@ -90,12 +76,6 @@ public class Frontend {
}
public void send(RequestPacket p, ResponseCallback callback) {
if (callback != null) {
p.setRequestedId(requestId.incrementAndGet());
synchronized (requests) {
requests.put(p.getRequestId(), callback);
}
}
send(p);
}
@@ -124,25 +104,6 @@ public class Frontend {
});
}
private void onConnectionStateChanged(boolean connected) {
new ConnectionStateUpdateIntent(connected).broadcast();
}
private void processIncoming(Packet p) {
Log.i("RX %s", p);
if (p instanceof ResponsePacket)
processResponse((ResponsePacket) p);
}
private void processResponse(ResponsePacket rp) {
ResponseCallback callback;
synchronized (requests) {
callback = requests.get(rp.getRequestId());
}
if (callback != null)
callback.onResponse(rp);
}
public interface ResponseCallback {
void onResponse(ResponsePacket p);
}

View File

@@ -50,7 +50,7 @@ public class LightspeedFrontendGUI extends Application {
public LightspeedFrontendGUI() {
frontend = new Frontend();
primaryView = new LightspeedPrimaryView(frontend);
IntentManager.getInstance().registerForIntent(ConnectionStateUpdateIntent.TYPE, (i) -> onConnectionUpdate(((ConnectionStateUpdateIntent) i).isConnected()));
IntentManager.getInstance().registerForIntent(ConnectionStateUpdateIntent.class, i -> onConnectionUpdate(((ConnectionStateUpdateIntent) i).isConnected()));
frontend.start();
}

View File

@@ -56,12 +56,11 @@ import com.projectswg.common.network.packets.response.ResponseDeploymentListPack
import com.projectswg.common.network.packets.response.ResponseServerListPacket;
import com.projectswg.common.utilities.TimeUtilities;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
import com.projectswg.lightspeed_frontend.communication.FrontendLightspeedSession.LightspeedSessionCallback;
import com.projectswg.lightspeed_frontend.data.SharedBuildData;
import com.projectswg.lightspeed_frontend.data.SharedDeploymentData;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
public class LightspeedFrontendText implements LightspeedSessionCallback {
public class LightspeedFrontendText {
private final FrontendCommunication communication;
private final Queue<Packet> inbound;
@@ -69,23 +68,9 @@ public class LightspeedFrontendText implements LightspeedSessionCallback {
public LightspeedFrontendText() {
communication = new FrontendCommunication();
communication.setCallback(this);
inbound = new ArrayDeque<>();
running = false;
}
@Override
public void onConnectionStateChanged(boolean connected) {
}
@Override
public void onIncomingPacket(Packet p) {
synchronized (inbound) {
inbound.add(p);
inbound.notifyAll();
}
}
public void run() {
running = true;

View File

@@ -35,21 +35,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
import com.projectswg.common.control.Assert;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.utilities.ThreadUtilities;
import com.projectswg.lightspeed.communication.LightspeedState;
import com.projectswg.lightspeed_frontend.communication.FrontendLightspeedSession.LightspeedSessionCallback;
public class FrontendCommunication {
private final AtomicBoolean running;
private ExecutorService executor;
private FrontendLightspeedSession session;
private LightspeedSessionCallback callback;
public FrontendCommunication() {
running = new AtomicBoolean(false);
session = null;
callback = null;
}
public boolean start() {
@@ -67,50 +61,24 @@ public class FrontendCommunication {
return false;
}
executor.shutdownNow();
if (session != null) {
session.disconnect();
session = null;
}
return true;
}
public boolean isConnected() {
return session != null && session.getState() == LightspeedState.CONNECTED;
}
public void setCallback(LightspeedSessionCallback callback) {
if (session != null)
session.setCallback(callback);
this.callback = callback;
return false;
}
public void connect(InetSocketAddress addr) {
if (addr == null)
throw new NullPointerException();
executor.execute(() -> {
Assert.test(callback != null);
if (session != null)
session.disconnect();
session = new FrontendLightspeedSession(addr);
session.setCallback(callback);
session.connect();
});
}
public void disconnect() {
executor.execute(() -> {
Assert.test(session != null);
if (session != null) {
session.disconnect();
session = null;
}
});
}
public void send(Packet p) {
Assert.test(session != null);
if (session != null)
session.send(p);
}
}

View File

@@ -1,153 +0,0 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore 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. *
* *
* Holocore 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 Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.lightspeed_frontend.communication;
import java.net.InetSocketAddress;
import com.projectswg.common.callback.CallbackManager;
import com.projectswg.common.info.Log;
import com.projectswg.common.network.TCPSocket;
import com.projectswg.common.network.TCPSocket.TCPSocketCallback;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.common.network.packets.init.ConnectedPacket;
import com.projectswg.common.network.packets.init.InitProtocolVersionPacket;
import com.projectswg.common.network.protocol.AlphaProtocol;
import com.projectswg.common.network.protocol.Protocol;
import com.projectswg.common.network.protocol.ProtocolFactory;
import com.projectswg.lightspeed.communication.LightspeedState;
public class FrontendLightspeedSession {
private final TCPSocket socket;
private final InetSocketAddress address;
private final CallbackManager<LightspeedSessionCallback> callbackManager;
private final Object stateMutex;
private LightspeedState state;
private Protocol protocol;
public FrontendLightspeedSession(InetSocketAddress address) {
this.socket = new TCPSocket(address, 4096);
this.address = address;
this.callbackManager = new CallbackManager<FrontendLightspeedSession.LightspeedSessionCallback>("lightspeed-session", 1);
this.stateMutex = new Object();
this.state = LightspeedState.DISCONNECTED;
this.protocol = ProtocolFactory.createProtocol(AlphaProtocol.VERSION);
this.socket.setCallback(new TCPSocketCallback() {
public void onConnected(TCPSocket socket) { onConnectionChanged(true); }
public void onDisconnected(TCPSocket socket) { onConnectionChanged(false); }
public void onIncomingData(TCPSocket socket, byte [] data) { onIncoming(data); }
});
}
public void setCallback(LightspeedSessionCallback callback) {
callbackManager.setCallback(callback);
}
public InetSocketAddress getAddress() {
return address;
}
public LightspeedState getState() {
synchronized (stateMutex) {
return state;
}
}
public void connect() {
Log.i("Connecting...");
updateState(LightspeedState.CONNECTING);
callbackManager.start();
socket.connect();
send(new InitProtocolVersionPacket(protocol.getVersion()));
synchronized (stateMutex) {
while (state == LightspeedState.CONNECTING) {
try {
stateMutex.wait();
} catch (InterruptedException e) {
break;
}
}
}
if (getState() == LightspeedState.CONNECTED)
Log.i("Connected.");
else
Log.e("Failed to connect.");
}
public void disconnect() {
socket.disconnect();
Log.i("Disconnected.");
updateState(LightspeedState.DISCONNECTED);
callbackManager.stop();
}
public void onIncoming(byte [] data) {
protocol.onIncomingData(data);
while (protocol.getPacketCount() > 0) {
Packet p = protocol.getNextPacket();
if (p != null)
processPacket(p);
}
}
public void send(Packet p) {
if (getState() == LightspeedState.CONNECTED || (p instanceof InitProtocolVersionPacket && getState() == LightspeedState.CONNECTING))
socket.send(protocol.preparePacket(p));
}
public interface LightspeedSessionCallback {
void onConnectionStateChanged(boolean connected);
void onIncomingPacket(Packet p);
}
private void processPacket(Packet p) {
if (p instanceof ConnectedPacket) {
updateState(LightspeedState.CONNECTED);
return;
}
callbackManager.callOnEach((callback) -> callback.onIncomingPacket(p));
}
private void onConnectionChanged(boolean connected) {
if (!connected)
disconnect();
}
private void updateState(LightspeedState state) {
LightspeedState old;
synchronized (stateMutex) {
old = this.state;
this.state = state;
stateMutex.notifyAll();
}
if (old == LightspeedState.CONNECTED && state == LightspeedState.DISCONNECTED)
callbackManager.callOnEach((callback) -> callback.onConnectionStateChanged(false));
else if (old == LightspeedState.CONNECTING && state == LightspeedState.CONNECTED)
callbackManager.callOnEach((callback) -> callback.onConnectionStateChanged(true));
}
}

View File

@@ -41,7 +41,6 @@ public class SharedBuildData {
private String buildString;
private String testString;
private double compileTime;
private boolean offline;
private boolean buildSuccess;
private boolean testSuccess;
private boolean cancelled;
@@ -54,7 +53,6 @@ public class SharedBuildData {
this.buildString = "";
this.testString = "";
this.compileTime = 0;
this.offline = false;
this.buildSuccess = false;
this.testSuccess = false;
this.cancelled = false;
@@ -97,10 +95,6 @@ public class SharedBuildData {
return compileTime;
}
public boolean isOffline() {
return offline;
}
public boolean isBuildSuccess() {
return buildSuccess;
}
@@ -137,10 +131,6 @@ public class SharedBuildData {
this.compileTime = compileTime;
}
public void setOffline(boolean offline) {
this.offline = offline;
}
public void setBuildSuccess(boolean buildSuccess) {
this.buildSuccess = buildSuccess;
}
@@ -169,4 +159,8 @@ public class SharedBuildData {
return ((SharedBuildData) o).getId() == id;
}
public String toString() {
return String.format("SharedBuildData[id=%d, server=%s]", id, getServer().getName());
}
}

View File

@@ -102,7 +102,7 @@ public class SharedDeploymentData {
}
public String toString() {
return String.format("DeploymentInformation[id=%d, server=%s, build=%d, start_time=%d, end_time=%d]", id, getServer().getName(), getBuild().getId(), startTime, endTime);
return String.format("SharedDeploymentData[id=%d, build=%d, server=%s, start_time=%d, end_time=%d]", id, getBuild().getId(), getServer().getName(), startTime, endTime);
}
}

View File

@@ -205,4 +205,8 @@ public class SharedServerData {
return ((SharedServerData) o).getName().equals(name);
}
public String toString() {
return String.format("SharedServerData[name=%s, dir=%s, args=%s]", name, directory, jvmArgs);
}
}

View File

@@ -87,7 +87,7 @@ public class LightspeedPrimaryView extends VBox implements Initializable {
this.initialized = new AtomicBoolean(false);
this.server = null;
this.address = null;
IntentManager.getInstance().registerForIntent(ConnectionStateUpdateIntent.TYPE, (i) -> update());
IntentManager.getInstance().registerForIntent(ConnectionStateUpdateIntent.class, i -> update());
FrontendFXMLLoader.load(this, "fxml/PrimaryView.fxml");
}

View File

@@ -202,11 +202,7 @@ public class BuildTab extends GridPane implements Initializable {
}
private void setMode() {
if (selectedBuildData == null) {
modeText.setText("N/A");
} else {
modeText.setText(selectedBuildData.isOffline() ? "Offline" : "Online");
}
modeText.setText("N/A");
}
private void setTests() {

View File

@@ -72,7 +72,7 @@ public class GeneralTab extends GridPane implements Initializable {
selectedServerData = null;
callback = null;
FrontendFXMLLoader.load(this, "fxml/primary_tabs/General.fxml");
IntentManager.getInstance().registerForIntent(ServerDataUpdateIntent.TYPE, (i) -> onIntent((ServerDataUpdateIntent) i));
IntentManager.getInstance().registerForIntent(ServerDataUpdateIntent.class, i -> onIntent((ServerDataUpdateIntent) i));
}
public void setFrontend(Frontend frontend) {

View File

@@ -31,8 +31,6 @@ import com.projectswg.common.control.Intent;
public class BuildDataUpdateIntent extends Intent {
public static final String TYPE = "BuildDataUpdateIntent";
private String serverName;
private long buildId;
private BuildUpdateType updateType;
@@ -42,7 +40,6 @@ public class BuildDataUpdateIntent extends Intent {
}
public BuildDataUpdateIntent(String serverName, long buildId, BuildUpdateType type) {
super(TYPE);
setServerName(serverName);
setBuildId(buildId);
setUpdateType(type);

View File

@@ -31,8 +31,6 @@ import com.projectswg.common.control.Intent;
public class ConnectionStateUpdateIntent extends Intent {
public static final String TYPE = "ConnectionStateUpdateIntent";
private boolean connected;
public ConnectionStateUpdateIntent() {
@@ -40,7 +38,6 @@ public class ConnectionStateUpdateIntent extends Intent {
}
public ConnectionStateUpdateIntent(boolean connected) {
super(TYPE);
setConnected(connected);
}

View File

@@ -31,8 +31,6 @@ import com.projectswg.common.control.Intent;
public class DeploymentDataUpdateIntent extends Intent {
public static final String TYPE = "DeploymentDataUpdateIntent";
private String serverName;
private long buildId;
private long deploymentId;
@@ -43,7 +41,6 @@ public class DeploymentDataUpdateIntent extends Intent {
}
public DeploymentDataUpdateIntent(String serverName, long buildId, long deploymentId, DeploymentUpdateType type) {
super(TYPE);
setServerName(serverName);
setBuildId(buildId);
setDeploymentId(deploymentId);

View File

@@ -31,8 +31,6 @@ import com.projectswg.common.control.Intent;
public class ServerDataUpdateIntent extends Intent {
public static final String TYPE = "ServerDataUpdateIntent";
private String name;
private ServerUpdateType updateType;
@@ -41,7 +39,6 @@ public class ServerDataUpdateIntent extends Intent {
}
public ServerDataUpdateIntent(String name, ServerUpdateType type) {
super(TYPE);
setName(name);
setUpdateType(type);
}

View File

@@ -31,9 +31,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
import com.projectswg.lightspeed_frontend.communication.FrontendLightspeedSession.LightspeedSessionCallback;
public class LinearTestFrontendCommunication {
@@ -45,10 +43,6 @@ public class LinearTestFrontendCommunication {
public LinearTestFrontendCommunication() {
communication = new FrontendCommunication();
connected = new AtomicBoolean(false);
communication.setCallback(new LightspeedSessionCallback() {
public void onIncomingPacket(Packet p) { LinearTestFrontendCommunication.this.onIncomingPacket(p); }
public void onConnectionStateChanged(boolean connected) { LinearTestFrontendCommunication.this.onConnectionStateChanged(connected); }
});
communication.start();
}
@@ -80,12 +74,4 @@ public class LinearTestFrontendCommunication {
return connected.get() == state;
}
private void onIncomingPacket(Packet p) {
}
private void onConnectionStateChanged(boolean connected) {
this.connected.set(connected);
}
}

View File

@@ -34,9 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Assert;
import org.junit.Test;
import com.projectswg.common.network.packets.Packet;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
import com.projectswg.lightspeed_frontend.communication.FrontendLightspeedSession.LightspeedSessionCallback;
public class TestConnect {
@@ -44,16 +42,6 @@ public class TestConnect {
public void testConnect() throws InterruptedException {
AtomicBoolean state = new AtomicBoolean(false);
FrontendCommunication communication = new FrontendCommunication();
communication.setCallback(new LightspeedSessionCallback() {
@Override
public void onIncomingPacket(Packet p) {
}
@Override
public void onConnectionStateChanged(boolean connected) {
state.set(connected);
}
});
communication.start();
communication.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), 44444));
int timeout = 1000;