mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-01-19 16:57:20 +07:00
Added server browser chat
Some work on in-game chat Separated Server into multiple systems (will be easier to transition to distributed servers later) Minor entity changes -- may transition to an ECS soon
This commit is contained in:
parent
cb11853a26
commit
a1e735031f
@ -41,5 +41,6 @@ public class Keys {
|
||||
public static final MappedKey Character = new MappedKey("Character", "character", Input.Keys.C, Input.Keys.A);
|
||||
public static final MappedKey Stash = new MappedKey("Stash", "stash", Input.Keys.NUMPAD_1);
|
||||
public static final MappedKey SwapWeapons = new MappedKey("SwapWeapons", "swap", Input.Keys.W);
|
||||
public static final MappedKey Enter = new MappedKey("Enter", "enter", Input.Keys.ENTER);
|
||||
|
||||
}
|
||||
|
@ -7,9 +7,11 @@ import com.badlogic.gdx.utils.Disposable;
|
||||
|
||||
public class Textures implements Disposable {
|
||||
public final Texture white;
|
||||
public final Texture modal;
|
||||
|
||||
public Textures() {
|
||||
white = createTexture(Diablo.colors.white);
|
||||
modal = createTexture(new Color(0.0f, 0.0f, 0.0f, 0.5f));
|
||||
}
|
||||
|
||||
public Texture createTexture(Color color) {
|
||||
@ -24,5 +26,6 @@ public class Textures implements Disposable {
|
||||
@Override
|
||||
public void dispose() {
|
||||
white.dispose();
|
||||
modal.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,12 @@ public class Direction {
|
||||
private Direction() {}
|
||||
|
||||
public static int radiansToDirection(float radians, int directions) {
|
||||
return radiansToDirection16(radians);
|
||||
switch (directions) {
|
||||
case 1: return 0;
|
||||
case 8: return radiansToDirection8(radians);
|
||||
case 16: return radiansToDirection16(radians);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final int DIRS_16[] = {7, 13, 2, 12, 6, 11, 1, 10, 5, 9, 0, 8, 4, 15, 3, 14};
|
||||
|
@ -23,6 +23,7 @@ import gdx.diablo.codec.DCC;
|
||||
import gdx.diablo.entity.Direction;
|
||||
import gdx.diablo.graphics.PaletteIndexedBatch;
|
||||
import gdx.diablo.item.Item;
|
||||
import gdx.diablo.map.DS1;
|
||||
import gdx.diablo.map.DT1.Tile;
|
||||
|
||||
public class Entity {
|
||||
@ -121,6 +122,18 @@ public class Entity {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public static Entity create(DS1 ds1, DS1.Object obj) {
|
||||
final int type = obj.type;
|
||||
switch (type) {
|
||||
case DS1.Object.DYNAMIC_TYPE:
|
||||
throw new UnsupportedOperationException("Unsupported type: " + type);
|
||||
case DS1.Object.STATIC_TYPE:
|
||||
return null;
|
||||
default:
|
||||
throw new AssertionError("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMode(String mode) {
|
||||
setMode(mode, mode);
|
||||
}
|
||||
@ -219,17 +232,42 @@ public class Entity {
|
||||
Gdx.app.log(TAG, path);
|
||||
|
||||
if (DEBUG_ASSETS) {
|
||||
AssetDescriptor<DCC> descriptor = new AssetDescriptor<>(path, DCC.class);
|
||||
final AssetDescriptor<DCC> descriptor = new AssetDescriptor<>(path, DCC.class);
|
||||
Diablo.assets.load(descriptor);
|
||||
Diablo.assets.finishLoadingAsset(descriptor);
|
||||
DCC dcc = Diablo.assets.get(descriptor);
|
||||
animation.setLayer(c, dcc);
|
||||
|
||||
/*Runnable loader = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Diablo.assets.isLoaded(descriptor)) {
|
||||
Gdx.app.postRunnable(this);
|
||||
return;
|
||||
}
|
||||
|
||||
DCC dcc = Diablo.assets.get(descriptor);
|
||||
animation.setLayer(c, dcc);
|
||||
|
||||
Item item = getItem(comp);
|
||||
if (item != null) {
|
||||
animation.getLayer(c).setTransform(item.charColormap, item.charColorIndex);
|
||||
}
|
||||
}
|
||||
};*/
|
||||
//Gdx.app.postRunnable(loader);
|
||||
}
|
||||
|
||||
Item item = getItem(comp);
|
||||
if (item != null) {
|
||||
animation.getLayer(layer.component).setTransform(item.charColormap, item.charColorIndex);
|
||||
}
|
||||
//if (BodyLoc.TORS.contains(c)) {
|
||||
Item item = getItem(comp);
|
||||
if (item != null) {
|
||||
// FIXME: colors don't look right for sorc Tirant circlet changing hair color
|
||||
// putting a ruby in a white circlet not change color on item or character
|
||||
// circlets and other items with hidden magic level might work different?
|
||||
animation.getLayer(layer.component).setTransform(item.charColormap, item.charColorIndex);
|
||||
//System.out.println(item.getName() + ": " + item.charColormap + " ; " + item.charColorIndex);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
dirty = 0;
|
||||
@ -272,7 +310,8 @@ public class Entity {
|
||||
}
|
||||
|
||||
public int getDirection() {
|
||||
return Direction.radiansToDirection(angle, 16);
|
||||
int numDirs = animation.getNumDirections();
|
||||
return Direction.radiansToDirection(angle, numDirs);
|
||||
}
|
||||
|
||||
public GridPoint2 origin() {
|
||||
|
@ -71,7 +71,7 @@ public class Player extends Entity {
|
||||
this.stats = new Stats();
|
||||
equipped.putAll(d2s.items.equipped);
|
||||
inventory.addAll(d2s.items.inventory);
|
||||
|
||||
setMode("TN");
|
||||
|
||||
for (Map.Entry<BodyLoc, Item> entry : equipped.entrySet()) {
|
||||
entry.getValue().load();
|
||||
|
@ -53,4 +53,8 @@ public enum BodyLoc {
|
||||
public int components() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public boolean contains(int component) {
|
||||
return components != 0 && (components & (1 << component)) != 0;
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
OrthographicCamera camera;
|
||||
InputProcessor inputProcessorTest;
|
||||
|
||||
//TextArea input;
|
||||
|
||||
//Char character;
|
||||
public Player player;
|
||||
@ -85,6 +86,19 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
public GameScreen(final Player player) {
|
||||
this.player = player;
|
||||
|
||||
/*
|
||||
input = new TextArea("", new TextArea.TextFieldStyle() {{
|
||||
this.font = Diablo.fonts.fontformal12;
|
||||
this.fontColor = Diablo.colors.white;
|
||||
this.background = new TextureRegionDrawable(Diablo.textures.modal);
|
||||
this.cursor = new TextureRegionDrawable(Diablo.textures.white);
|
||||
}});
|
||||
input.setSize(Diablo.VIRTUAL_WIDTH * 0.75f, Diablo.fonts.fontformal12.getLineHeight() * 3);
|
||||
input.setPosition(Diablo.VIRTUAL_WIDTH_CENTER - input.getWidth() / 2, 100);
|
||||
input.setAlignment(Align.topLeft);
|
||||
input.setVisible(false);
|
||||
*/
|
||||
|
||||
escapePanel = new EscapePanel();
|
||||
|
||||
controlPanel = new ControlPanel(this);
|
||||
@ -116,12 +130,14 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
|
||||
stage = new Stage(Diablo.viewport, Diablo.batch);
|
||||
if (mobilePanel != null) stage.addActor(mobilePanel);
|
||||
//stage.addActor(input);
|
||||
stage.addActor(controlPanel);
|
||||
stage.addActor(escapePanel);
|
||||
stage.addActor(inventoryPanel);
|
||||
stage.addActor(characterPanel);
|
||||
stage.addActor(stashPanel);
|
||||
controlPanel.toFront();
|
||||
//input.toFront();
|
||||
escapePanel.toFront();
|
||||
|
||||
if (Gdx.app.getType() == Application.ApplicationType.Android || DEBUG_TOUCHPAD) {
|
||||
@ -141,7 +157,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
public void changed(ChangeEvent event, Actor actor) {
|
||||
float x = touchpad.getKnobPercentX();
|
||||
float y = touchpad.getKnobPercentY();
|
||||
if (x == 0 && y == 0) {
|
||||
if (x == 0 && y == 0 || UIUtils.shift()) {
|
||||
player.setMode("TN");
|
||||
return;
|
||||
//} else if (-0.5f < x && x < 0.5f
|
||||
@ -171,6 +187,20 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
} else {
|
||||
escapePanel.setVisible(true);
|
||||
}
|
||||
/*} else if (key == Keys.Enter) {
|
||||
boolean visible = !input.isVisible();
|
||||
if (!visible) {
|
||||
String text = input.getText();
|
||||
if (!text.isEmpty()) {
|
||||
Gdx.app.debug(TAG, text);
|
||||
input.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
input.setVisible(visible);
|
||||
if (visible) {
|
||||
stage.setKeyboardFocus(input);
|
||||
}*/
|
||||
} else if (key == Keys.Inventory) {
|
||||
inventoryPanel.setVisible(!inventoryPanel.isVisible());
|
||||
} else if (key == Keys.Character) {
|
||||
@ -292,12 +322,14 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
Keys.Character.addStateListener(mappedKeyStateListener);
|
||||
Keys.Stash.addStateListener(mappedKeyStateListener);
|
||||
Keys.SwapWeapons.addStateListener(mappedKeyStateListener);
|
||||
Keys.Enter.addStateListener(mappedKeyStateListener);
|
||||
Diablo.input.addProcessor(stage);
|
||||
Diablo.input.addProcessor(inputProcessorTest);
|
||||
|
||||
updateTask = Timer.schedule(new Timer.Task() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (UIUtils.shift()) return;
|
||||
player.move();
|
||||
mapRenderer.setPosition(player.origin());
|
||||
}
|
||||
@ -311,6 +343,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
|
||||
Keys.Character.removeStateListener(mappedKeyStateListener);
|
||||
Keys.Stash.removeStateListener(mappedKeyStateListener);
|
||||
Keys.SwapWeapons.removeStateListener(mappedKeyStateListener);
|
||||
Keys.Enter.removeStateListener(mappedKeyStateListener);
|
||||
Diablo.input.removeProcessor(stage);
|
||||
Diablo.input.removeProcessor(inputProcessorTest);
|
||||
|
||||
|
@ -7,6 +7,7 @@ import com.badlogic.gdx.ScreenAdapter;
|
||||
import com.badlogic.gdx.assets.AssetDescriptor;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.net.HttpRequestBuilder;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.EventListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
@ -24,7 +25,14 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.SerializationException;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
@ -34,8 +42,10 @@ import gdx.diablo.graphics.PaletteIndexedBatch;
|
||||
import gdx.diablo.loader.DC6Loader;
|
||||
import gdx.diablo.server.Account;
|
||||
import gdx.diablo.server.Session;
|
||||
import gdx.diablo.server.SessionError;
|
||||
import gdx.diablo.util.EventUtils;
|
||||
import gdx.diablo.widget.Label;
|
||||
import gdx.diablo.widget.TextArea;
|
||||
import gdx.diablo.widget.TextButton;
|
||||
import gdx.diablo.widget.TextField;
|
||||
|
||||
@ -70,6 +80,13 @@ public class LobbyScreen extends ScreenAdapter {
|
||||
|
||||
private Stage stage;
|
||||
|
||||
private TextArea taChatOutput;
|
||||
private TextField tfChatInput;
|
||||
|
||||
private Socket socket;
|
||||
private PrintWriter out;
|
||||
private BufferedReader in;
|
||||
|
||||
public LobbyScreen(Account account) {
|
||||
Diablo.assets.load(waitingroombkgdDescriptor);
|
||||
Diablo.assets.load(blankbckgDescriptor);
|
||||
@ -262,7 +279,37 @@ public class LobbyScreen extends ScreenAdapter {
|
||||
desc = tfDesc.getText();
|
||||
}})
|
||||
.build();
|
||||
Gdx.net.sendHttpRequest(request, null);
|
||||
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
|
||||
@Override
|
||||
public void handleHttpResponse(Net.HttpResponse httpResponse) {
|
||||
String response = httpResponse.getResultAsString();
|
||||
try {
|
||||
Session session = new Json().fromJson(Session.class, response);
|
||||
Gdx.app.log(TAG, "create-session " + response);
|
||||
|
||||
Socket socket = null;
|
||||
try {
|
||||
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, session.host, session.port, null);
|
||||
Gdx.app.log(TAG, "create-session connect " + session.host + ":" + session.port + " " + socket.isConnected());
|
||||
} finally {
|
||||
if (socket != null) socket.dispose();
|
||||
}
|
||||
} catch (SerializationException e) {
|
||||
SessionError error = new Json().fromJson(SessionError.class, response);
|
||||
Gdx.app.log(TAG, "create-session " + error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable t) {
|
||||
Gdx.app.log(TAG, "create-session " + t.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled() {
|
||||
Gdx.app.log(TAG, "create-session " + "cancelled");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
tfGameName.addListener(new ChangeListener() {
|
||||
@ -420,12 +467,54 @@ public class LobbyScreen extends ScreenAdapter {
|
||||
}};
|
||||
stage.addActor(right);
|
||||
|
||||
final Table left = new Table() {{
|
||||
setSize(350, 332);
|
||||
setPosition(55, 115);
|
||||
add(taChatOutput = new TextArea("", new TextArea.TextFieldStyle() {{
|
||||
font = Diablo.fonts.fontformal10;
|
||||
fontColor = Diablo.colors.white;
|
||||
cursor = new TextureRegionDrawable(Diablo.textures.white);
|
||||
}}) {{
|
||||
setDisabled(true);
|
||||
}}).grow().row();
|
||||
add(tfChatInput = new TextField("", textFieldStyle) {{
|
||||
setTextFieldListener(new TextFieldListener() {
|
||||
@Override
|
||||
public void keyTyped(com.badlogic.gdx.scenes.scene2d.ui.TextField textField, char c) {
|
||||
if (c == '\r' || c == '\n') {
|
||||
if (socket != null && socket.isConnected()) {
|
||||
out.println(textField.getText());
|
||||
textField.setText("");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}}).growX();
|
||||
}};
|
||||
stage.addActor(left);
|
||||
stage.setKeyboardFocus(tfChatInput);
|
||||
|
||||
Diablo.input.addProcessor(stage);
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
try {
|
||||
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, "hydra", 6113, null);
|
||||
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(socket.getOutputStream(), true);
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.error(TAG, t.getMessage());
|
||||
taChatOutput.appendText(t.getMessage());
|
||||
taChatOutput.appendText("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
Diablo.input.removeProcessor(stage);
|
||||
IOUtils.closeQuietly(out);
|
||||
if (socket != null) socket.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -442,6 +531,17 @@ public class LobbyScreen extends ScreenAdapter {
|
||||
|
||||
@Override
|
||||
public void render(float delta) {
|
||||
if (in != null) {
|
||||
try {
|
||||
for (String str; in.ready() && (str = in.readLine()) != null;) {
|
||||
taChatOutput.appendText(str);
|
||||
taChatOutput.appendText("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Gdx.app.error(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
PaletteIndexedBatch b = Diablo.batch;
|
||||
b.begin(Diablo.palettes.act1);
|
||||
b.draw(waitingroombkgd, Diablo.VIRTUAL_WIDTH_CENTER - (waitingroombkgd.getRegionWidth() / 2), Diablo.VIRTUAL_HEIGHT - waitingroombkgd.getRegionHeight() + 72);
|
||||
|
10
core/src/gdx/diablo/server/Message.java
Normal file
10
core/src/gdx/diablo/server/Message.java
Normal file
@ -0,0 +1,10 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
public class Message {
|
||||
|
||||
public String from;
|
||||
public String text;
|
||||
|
||||
private Message() {}
|
||||
|
||||
}
|
@ -2,9 +2,11 @@ package gdx.diablo.server;
|
||||
|
||||
public class Session {
|
||||
|
||||
private String name;
|
||||
private String password;
|
||||
private String desc;
|
||||
public String name;
|
||||
public String password;
|
||||
public String desc;
|
||||
public String host;
|
||||
public int port;
|
||||
|
||||
private Session() {}
|
||||
|
||||
@ -18,9 +20,13 @@ public class Session {
|
||||
desc = builder.desc;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
return getName();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
27
core/src/gdx/diablo/server/SessionError.java
Normal file
27
core/src/gdx/diablo/server/SessionError.java
Normal file
@ -0,0 +1,27 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
public class SessionError {
|
||||
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
private SessionError() {}
|
||||
|
||||
public SessionError(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return code + " " + message;
|
||||
}
|
||||
}
|
26
core/src/gdx/diablo/widget/TextArea.java
Normal file
26
core/src/gdx/diablo/widget/TextArea.java
Normal file
@ -0,0 +1,26 @@
|
||||
package gdx.diablo.widget;
|
||||
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
|
||||
public class TextArea extends com.badlogic.gdx.scenes.scene2d.ui.TextArea implements Disposable {
|
||||
|
||||
public TextArea(TextFieldStyle style) {
|
||||
this("", style);
|
||||
}
|
||||
|
||||
public TextArea(String text, TextFieldStyle style) {
|
||||
super(text, style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
|
||||
}
|
||||
|
||||
public static class TextFieldStyle extends com.badlogic.gdx.scenes.scene2d.ui.TextArea.TextFieldStyle {
|
||||
public TextFieldStyle() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,11 @@ import gdx.diablo.COFs;
|
||||
import gdx.diablo.Diablo;
|
||||
import gdx.diablo.Files;
|
||||
import gdx.diablo.codec.D2S;
|
||||
import gdx.diablo.codec.DC6;
|
||||
import gdx.diablo.codec.DCC;
|
||||
import gdx.diablo.codec.StringTBLs;
|
||||
import gdx.diablo.loader.DC6Loader;
|
||||
import gdx.diablo.loader.DCCLoader;
|
||||
import gdx.diablo.mpq.MPQFileHandleResolver;
|
||||
|
||||
public class EntityTest {
|
||||
@ -33,7 +37,12 @@ public class EntityTest {
|
||||
resolver.add(Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II\\patch_d2.mpq"));
|
||||
resolver.add(Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II\\d2exp.mpq"));
|
||||
resolver.add(Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II\\d2data.mpq"));
|
||||
Diablo.assets = new AssetManager(resolver);
|
||||
resolver.add(Gdx.files.absolute("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Diablo II\\d2char.mpq"));
|
||||
|
||||
Diablo.assets = new AssetManager();
|
||||
Diablo.assets.setLoader(DCC.class, new DCCLoader(Diablo.mpqs));
|
||||
Diablo.assets.setLoader(DC6.class, new DC6Loader(Diablo.mpqs));
|
||||
|
||||
Diablo.cofs = new COFs(Diablo.assets);
|
||||
Diablo.files = new Files(Diablo.assets);
|
||||
Diablo.string = new StringTBLs(resolver);
|
||||
|
@ -4,7 +4,7 @@ sourceCompatibility = 1.7
|
||||
sourceSets.main.java.srcDirs = [ "src/" ]
|
||||
sourceSets.test.java.srcDirs = [ "test/" ]
|
||||
|
||||
project.ext.mainClassName = "gdx.diablo.server.Server"
|
||||
project.ext.mainClassName = "gdx.diablo.server.ServerBrowser"
|
||||
project.ext.assetsDir = new File("../android/assets");
|
||||
|
||||
task run(dependsOn: classes, type: JavaExec) {
|
||||
|
131
server/src/gdx/diablo/server/ChatServer.java
Normal file
131
server/src/gdx/diablo/server/ChatServer.java
Normal file
@ -0,0 +1,131 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplication;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.net.ServerSocket;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ChatServer extends ApplicationAdapter {
|
||||
private static final String TAG = "ChatServer";
|
||||
|
||||
public static void main(String[] args) {
|
||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||
new HeadlessApplication(new ChatServer(), config);
|
||||
}
|
||||
|
||||
private final Json json = new Json();
|
||||
private Set<PrintWriter> clients = new CopyOnWriteArraySet<>();
|
||||
|
||||
ThreadGroup clientThreads;
|
||||
ServerSocket server;
|
||||
Thread thread;
|
||||
AtomicBoolean kill;
|
||||
|
||||
ChatServer() {}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
final Calendar calendar = Calendar.getInstance();
|
||||
DateFormat format = DateFormat.getDateTimeInstance();
|
||||
Gdx.app.log(TAG, format.format(calendar.getTime()));
|
||||
|
||||
try {
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
Gdx.app.log(TAG, "IP Address: " + address.getHostAddress());
|
||||
Gdx.app.log(TAG, "Host Name: " + address.getHostName());
|
||||
} catch (UnknownHostException e) {
|
||||
Gdx.app.error(TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
clientThreads = new ThreadGroup("Clients");
|
||||
|
||||
kill = new AtomicBoolean(false);
|
||||
server = Gdx.net.newServerSocket(Net.Protocol.TCP, 6113, null);
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!kill.get()) {
|
||||
Socket socket = server.accept(null);
|
||||
Gdx.app.log(TAG, "CONNECT " + socket.getRemoteAddress());
|
||||
new Client(socket).start();
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setName("ChatServer");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.log(TAG, "shutting down...");
|
||||
kill.set(true);
|
||||
try {
|
||||
thread.join();
|
||||
} catch (Throwable ignored) {}
|
||||
server.dispose();
|
||||
}
|
||||
|
||||
private class Client extends Thread {
|
||||
Socket socket;
|
||||
BufferedReader in;
|
||||
PrintWriter out;
|
||||
|
||||
public Client(Socket socket) {
|
||||
super(clientThreads, "Client-" + String.format("%08X", MathUtils.random(1, Integer.MAX_VALUE)));
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(socket.getOutputStream(), true);
|
||||
clients.add(out);
|
||||
|
||||
for (String input; (input = in.readLine()) != null; ) {
|
||||
String message = "MESSAGE " + socket.getRemoteAddress() + ": " + input;
|
||||
Gdx.app.log(TAG, message);
|
||||
for (PrintWriter client : clients) {
|
||||
client.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.log(TAG, "ERROR " + socket.getRemoteAddress() + ": " + t.getMessage());
|
||||
} finally {
|
||||
String message = "DISCONNECT " + socket.getRemoteAddress();
|
||||
Gdx.app.log(TAG, message);
|
||||
for (PrintWriter client : clients) {
|
||||
client.println(message);
|
||||
}
|
||||
//IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(out);
|
||||
if (out != null) clients.remove(out);
|
||||
if (socket != null) socket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,173 +1,39 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplication;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
||||
import com.badlogic.gdx.net.ServerSocket;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class Server extends ApplicationAdapter {
|
||||
public class Server extends Thread {
|
||||
private static final String TAG = "Server";
|
||||
|
||||
public static void main(String[] args) {
|
||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||
new HeadlessApplication(new Server(), config);
|
||||
}
|
||||
|
||||
private final Json json = new Json();
|
||||
private Array<Session> sessions = new Array<>(new Session[] {
|
||||
new Session("Kmbaal-33"),
|
||||
new Session("Cbaalz73"),
|
||||
new Session("Killin Foos"),
|
||||
new Session("Skulders 4 Scri"),
|
||||
});
|
||||
|
||||
ServerSocket server;
|
||||
Thread serverThread;
|
||||
AtomicBoolean killServer;
|
||||
AtomicBoolean kill;
|
||||
int port;
|
||||
|
||||
Server() {}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
DateFormat format = DateFormat.getDateTimeInstance();
|
||||
Gdx.app.log(TAG, format.format(calendar.getTime()));
|
||||
|
||||
try {
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
Gdx.app.log(TAG, "IP Address: " + address.getHostAddress());
|
||||
Gdx.app.log(TAG, "Host Name: " + address.getHostName());
|
||||
} catch (UnknownHostException e) {
|
||||
Gdx.app.error(TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
Gdx.app.log(TAG, "awaiting connection...");
|
||||
|
||||
server = Gdx.net.newServerSocket(Net.Protocol.TCP, 6112, null);
|
||||
killServer = new AtomicBoolean(false);
|
||||
serverThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!killServer.get()) {
|
||||
Socket socket = null;
|
||||
BufferedReader in = null;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
socket = server.accept(null);
|
||||
|
||||
Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress());
|
||||
|
||||
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(socket.getOutputStream(), false);
|
||||
|
||||
String statusLine = in.readLine();
|
||||
Gdx.app.log(TAG, statusLine);
|
||||
String[] parts = statusLine.split("\\s+", 3);
|
||||
|
||||
String path = parts[1];
|
||||
if (path.equals("/get-sessions")) {
|
||||
getSessions(out);
|
||||
} else if (path.equals("/create-session")) {
|
||||
createSession(in, out);
|
||||
} else if (path.equals("/find-server")) {
|
||||
findServer(out);
|
||||
} else if (path.equals("/login")) {
|
||||
login(in, out);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.error(TAG, t.getMessage(), t);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(out);
|
||||
//IOUtils.closeQuietly(in);
|
||||
if (socket != null) socket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
serverThread.start();
|
||||
Server(ThreadGroup group, String name, int port) {
|
||||
super(group, name);
|
||||
this.port = port;
|
||||
kill = new AtomicBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.log(TAG, "shutting down...");
|
||||
killServer.set(true);
|
||||
try {
|
||||
serverThread.join();
|
||||
} catch (Throwable ignored) {}
|
||||
server.dispose();
|
||||
}
|
||||
|
||||
private void getSessions(PrintWriter out) {
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(sessions));
|
||||
}
|
||||
|
||||
private void createSession(BufferedReader in, PrintWriter out) {
|
||||
String content = getContent(in);
|
||||
//System.out.println(content);
|
||||
Session.Builder builder = json.fromJson(Session.Builder.class, content);
|
||||
|
||||
sessions.add(builder.build());
|
||||
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(builder.build()));
|
||||
}
|
||||
|
||||
private void findServer(PrintWriter out) {
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
}
|
||||
|
||||
private void login(BufferedReader in, PrintWriter out) {
|
||||
String content = getContent(in);
|
||||
//System.out.println(content);
|
||||
Account.Builder builder = json.fromJson(Account.Builder.class, content);
|
||||
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(builder.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: parse packet content-length and read that many chars into a string
|
||||
*/
|
||||
public String getContent(BufferedReader reader) {
|
||||
try {
|
||||
int length = -1;
|
||||
for (String str; (str = reader.readLine()) != null && !str.isEmpty();) {
|
||||
if (StringUtils.startsWithIgnoreCase(str, "Content-Length:")) {
|
||||
str = StringUtils.replaceIgnoreCase(str, "Content-Length:", "").trim();
|
||||
length = NumberUtils.toInt(str, length);
|
||||
}
|
||||
public void run() {
|
||||
server = Gdx.net.newServerSocket(Net.Protocol.TCP, port, null);
|
||||
while (!kill.get()) {
|
||||
Socket client = null;
|
||||
try {
|
||||
client = server.accept(null);
|
||||
Gdx.app.log(getName(), "connection from " + client.getRemoteAddress());
|
||||
} finally {
|
||||
if (client != null) client.dispose();
|
||||
}
|
||||
//return reader.readLine();
|
||||
char[] chars = new char[length];
|
||||
reader.read(chars);
|
||||
return new String(chars);
|
||||
} catch (IOException e) {
|
||||
Gdx.app.error(TAG, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
179
server/src/gdx/diablo/server/ServerBrowser.java
Normal file
179
server/src/gdx/diablo/server/ServerBrowser.java
Normal file
@ -0,0 +1,179 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Net;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplication;
|
||||
import com.badlogic.gdx.backends.headless.HeadlessApplicationConfiguration;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.net.ServerSocket;
|
||||
import com.badlogic.gdx.net.Socket;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ServerBrowser extends ApplicationAdapter {
|
||||
private static final String TAG = "Server";
|
||||
|
||||
public static void main(String[] args) {
|
||||
HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration();
|
||||
new HeadlessApplication(new ServerBrowser(), config);
|
||||
}
|
||||
|
||||
private final Json json = new Json();
|
||||
private Map<String, Session> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
ServerSocket server;
|
||||
Thread thread;
|
||||
AtomicBoolean kill;
|
||||
|
||||
ThreadGroup sessionGroup = new ThreadGroup("Sessions");
|
||||
private Map<String, Thread> servers = new ConcurrentHashMap<>();
|
||||
|
||||
ServerBrowser() {}
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
final Calendar calendar = Calendar.getInstance();
|
||||
DateFormat format = DateFormat.getDateTimeInstance();
|
||||
Gdx.app.log(TAG, format.format(calendar.getTime()));
|
||||
|
||||
try {
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
Gdx.app.log(TAG, "IP Address: " + address.getHostAddress());
|
||||
Gdx.app.log(TAG, "Host Name: " + address.getHostName());
|
||||
} catch (UnknownHostException e) {
|
||||
Gdx.app.error(TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
kill = new AtomicBoolean(false);
|
||||
server = Gdx.net.newServerSocket(Net.Protocol.TCP, 6112, null);
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!kill.get()) {
|
||||
Socket socket = null;
|
||||
BufferedReader in = null;
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
socket = server.accept(null);
|
||||
Gdx.app.log(TAG, "connection from " + socket.getRemoteAddress());
|
||||
|
||||
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(socket.getOutputStream(), false);
|
||||
|
||||
String statusLine = in.readLine();
|
||||
Gdx.app.log(TAG, statusLine);
|
||||
String[] parts = statusLine.split("\\s+", 3);
|
||||
|
||||
String path = parts[1];
|
||||
if (path.equals("/get-sessions")) {
|
||||
getSessions(out);
|
||||
} else if (path.equals("/create-session")) {
|
||||
createSession(in, out);
|
||||
} else if (path.equals("/find-server")) {
|
||||
findServer(out);
|
||||
} else if (path.equals("/login")) {
|
||||
login(in, out);
|
||||
} else if (path.equals("/chat")) {
|
||||
chat(in, out);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Gdx.app.error(TAG, t.getMessage(), t);
|
||||
} finally {
|
||||
//IOUtils.closeQuietly(in);
|
||||
IOUtils.closeQuietly(out);
|
||||
if (socket != null) socket.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setName("ServerBrowser");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.app.log(TAG, "shutting down...");
|
||||
kill.set(true);
|
||||
try {
|
||||
thread.join();
|
||||
} catch (Throwable ignored) {}
|
||||
server.dispose();
|
||||
}
|
||||
|
||||
private void getSessions(PrintWriter out) {
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(sessions.values()));
|
||||
}
|
||||
|
||||
private void createSession(BufferedReader in, PrintWriter out) {
|
||||
String content = ServerUtils.getContent(in);
|
||||
Session.Builder builder = json.fromJson(Session.Builder.class, content);
|
||||
|
||||
if (sessions.containsKey(builder.name)) {
|
||||
SessionError error = new SessionError(5138, "A game already exists with that name");
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(error));
|
||||
return;
|
||||
} else if (sessions.size() >= 2) {
|
||||
SessionError error = new SessionError(5140, "No game server available");
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(error));
|
||||
return;
|
||||
}
|
||||
|
||||
Session session = builder.build();
|
||||
session.host = "hydra";
|
||||
session.port = 6114 + sessions.size();
|
||||
sessions.put(session.getName(), session);
|
||||
|
||||
String id = String.format("%08x", MathUtils.random(Integer.MAX_VALUE - 1));
|
||||
Server server = new Server(sessionGroup, "Session-" + id, session.port);
|
||||
server.start();
|
||||
servers.put(session.getName(), server);
|
||||
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(session));
|
||||
}
|
||||
|
||||
private void findServer(PrintWriter out) {
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
}
|
||||
|
||||
private void login(BufferedReader in, PrintWriter out) {
|
||||
String content = ServerUtils.getContent(in);
|
||||
//System.out.println(content);
|
||||
Account.Builder builder = json.fromJson(Account.Builder.class, content);
|
||||
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
out.print(json.toJson(builder.build()));
|
||||
}
|
||||
|
||||
private void chat(BufferedReader in, PrintWriter out) {
|
||||
out.print("HTTP/1.1 200\r\n");
|
||||
out.print("\r\n");
|
||||
}
|
||||
}
|
35
server/src/gdx/diablo/server/ServerUtils.java
Normal file
35
server/src/gdx/diablo/server/ServerUtils.java
Normal file
@ -0,0 +1,35 @@
|
||||
package gdx.diablo.server;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ServerUtils {
|
||||
private static final String TAG = "ServerUtils";
|
||||
|
||||
private ServerUtils() {}
|
||||
|
||||
public static String getContent(BufferedReader reader) {
|
||||
try {
|
||||
int length = -1;
|
||||
for (String str; (str = reader.readLine()) != null && !str.isEmpty();) {
|
||||
if (StringUtils.startsWithIgnoreCase(str, "Content-Length:")) {
|
||||
str = StringUtils.replaceIgnoreCase(str, "Content-Length:", "").trim();
|
||||
length = NumberUtils.toInt(str, length);
|
||||
}
|
||||
}
|
||||
|
||||
char[] chars = new char[length];
|
||||
reader.read(chars);
|
||||
return new String(chars);
|
||||
} catch (IOException e) {
|
||||
Gdx.app.error(TAG, e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user