Added functional networking with multiple clients in a server rendered correctly

Fixed bug with Map.find(int) returning actual GridPoint -- now returns a copy
Added workaround for set(String) in Cvar when generic type is String
Transition to entity package with refinements
This commit is contained in:
Collin Smith 2019-02-08 20:56:04 -08:00
parent e23532266f
commit 86c4e6d8ef
25 changed files with 1003 additions and 381 deletions

View File

@ -104,6 +104,7 @@ public class Client extends Game {
private boolean forceWindowed;
private boolean forceDrawFps;
private byte drawFpsMethod;
private String realm;
public Client(FileHandle home) {
this(home, Diablo.VIRTUAL_WIDTH, Diablo.VIRTUAL_HEIGHT);
@ -140,6 +141,16 @@ public class Client extends Game {
forceDrawFps = b;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
if (!this.realm.equalsIgnoreCase(realm)) {
Cvars.Client.Realm.setString(realm);
}
}
@Override
public void create() {
Gdx.app.setLogLevel(Application.LOG_DEBUG);
@ -491,6 +502,13 @@ public class Client extends Game {
batch.setGamma(to);
}
});
Cvars.Client.Realm.addStateListener(new CvarStateAdapter<String>() {
@Override
public void onChanged(Cvar<String> cvar, String from, String to) {
realm = to;
}
});
}
public static class InputProcessor extends InputMultiplexer {

View File

@ -61,6 +61,12 @@ public class Cvars {
.validator(Validator.ACCEPT_NON_NULL)
.build();
Cvar<String> Realm = Cvar.builder(String.class)
.alias("Client.Realm")
.description("Realm to connect to.")
.defaultValue("hydra")
.build();
interface Console {
Cvar<String> Font = Cvar.builder(String.class)
.alias("Client.Console.Font")

View File

@ -133,6 +133,11 @@ public class Cvar<T> implements SuggestionProvider {
}
}
// FIXME: Workaround for issue calling set(String) when <T> is also String
public void setString(@NonNull String str) {
set(str);
}
public void set(@NonNull String str, @NonNull StringSerializer deserializer) {
try {
T value = ((StringSerializer<T>) deserializer).deserialize(str);

View File

@ -0,0 +1,46 @@
package gdx.diablo.entity;
import com.badlogic.gdx.Gdx;
public enum Component {
HD,
TR,
LG,
RA,
LA,
RH,
LH,
SH,
S1,
S2,
S3,
S4,
S5,
S6,
S7,
S8;
public static Component valueOf(int i) {
switch (i) {
case 0x0: return HD;
case 0x1: return TR;
case 0x2: return LG;
case 0x3: return RA;
case 0x4: return LA;
case 0x5: return RH;
case 0x6: return LH;
case 0x7: return SH;
case 0x8: return S1;
case 0x9: return S2;
case 0xA: return S3;
case 0xB: return S4;
case 0xC: return S5;
case 0xD: return S6;
case 0xE: return S7;
case 0xF: return S8;
default:
Gdx.app.error("Component", "Unknown component: " + i);
return null;
}
}
}

View File

@ -0,0 +1,336 @@
package gdx.diablo.entity;
import android.support.annotation.CallSuper;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import gdx.diablo.Diablo;
import gdx.diablo.codec.Animation;
import gdx.diablo.codec.COF;
import gdx.diablo.codec.COFD2;
import gdx.diablo.codec.DCC;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.map.DT1.Tile;
public class Entity {
private static final String TAG = "Entity";
private static final boolean DEBUG = true;
private static final boolean DEBUG_COMPONENTS = DEBUG && true;
private static final boolean DEBUG_COF = DEBUG && !true;
private static final boolean DEBUG_DIRTY = DEBUG && true;
private static final boolean DEBUG_ASSETS = DEBUG && true;
private static final boolean DEBUG_STATE = DEBUG && true;
protected enum EntType {
OBJECT("OBJECT"),
MONSTER("MONSTER"),
PLAYER("CHARS");
public final String PATH;
EntType(String path) {
PATH = "data\\global\\" + path + "\\";
}
}
public static final class Dirty {
public static final int NONE = 0;
public static final int HD = 1 << 0;
public static final int TR = 1 << 1;
public static final int LG = 1 << 2;
public static final int RA = 1 << 3;
public static final int LA = 1 << 4;
public static final int RH = 1 << 5;
public static final int LH = 1 << 6;
public static final int SH = 1 << 7;
public static final int S1 = 1 << 8;
public static final int S2 = 1 << 9;
public static final int S3 = 1 << 10;
public static final int S4 = 1 << 11;
public static final int S5 = 1 << 12;
public static final int S6 = 1 << 13;
public static final int S7 = 1 << 14;
public static final int S8 = 1 << 15;
public static final int ALL = 0xFFFF;
public static String toString(int bits) {
StringBuilder builder = new StringBuilder();
if (bits == NONE) {
builder.append("NONE");
} else {
if ((bits & HD) == HD) builder.append("HD").append("|");
if ((bits & TR) == TR) builder.append("TR").append("|");
if ((bits & LG) == LG) builder.append("LG").append("|");
if ((bits & RA) == RA) builder.append("RA").append("|");
if ((bits & LA) == LA) builder.append("LA").append("|");
if ((bits & RH) == RH) builder.append("RH").append("|");
if ((bits & LH) == LH) builder.append("LH").append("|");
if ((bits & SH) == SH) builder.append("SH").append("|");
if ((bits & S1) == S1) builder.append("S1").append("|");
if ((bits & S2) == S2) builder.append("S2").append("|");
if ((bits & S3) == S3) builder.append("S3").append("|");
if ((bits & S4) == S4) builder.append("S4").append("|");
if ((bits & S5) == S5) builder.append("S5").append("|");
if ((bits & S6) == S6) builder.append("S6").append("|");
if ((bits & S7) == S7) builder.append("S7").append("|");
if ((bits & S8) == S8) builder.append("S8").append("|");
if (builder.length() > 0) builder.setLength(builder.length() - 1);
}
return builder.toString();
}
public static boolean isDirty(int flags, int component) {
return ((1 << component) & flags) != 0;
}
}
protected static final String DEFAULT_LAYER = "LIT";
private static final String[] DEFAULT_LAYERS;
static {
DEFAULT_LAYERS = new String[16];
Arrays.fill(DEFAULT_LAYERS, DEFAULT_LAYER);
}
String type;
EntType entType;
int dirty;
String mode;
String code;
String layers[];
String weaponClass;
Vector3 position = new Vector3();
Vector3 velocity = new Vector3();
float angle = MathUtils.PI * 3 / 2;
Animation animation;
Entity(String type) {
this(type, EntType.OBJECT);
}
Entity(String type, EntType entType) {
this.type = type;
this.entType = entType;
mode = code = "NU";
weaponClass = "HTH";
layers = DEFAULT_LAYERS;
invalidate();
}
public void setMode(String mode) {
setMode(mode, mode);
}
public void setMode(String mode, String code) {
if (!this.mode.equalsIgnoreCase(mode)) {
if (DEBUG_STATE) Gdx.app.debug(TAG, "mode: " + this.mode + " -> " + mode);
this.mode = mode;
invalidate();
}
this.code = code;
}
public void setWeaponClass(String weaponClass) {
if (!this.weaponClass.equalsIgnoreCase(weaponClass)) {
if (DEBUG_STATE) Gdx.app.debug(TAG, "weaponClass: " + this.weaponClass + " -> " + weaponClass);
this.weaponClass = weaponClass;
invalidate();
}
}
public void setArmType(Component component, String armType) {
if (layers == DEFAULT_LAYERS) {
if (!DEFAULT_LAYER.equalsIgnoreCase(armType)) {
layers = ArrayUtils.clone(DEFAULT_LAYERS);
} else {
return;
}
}
int ordinal = component.ordinal();
if (layers[ordinal].equalsIgnoreCase(armType)) {
return;
}
if (DEBUG_COMPONENTS) Gdx.app.debug(TAG, component + " " + layers[ordinal] + " -> " + armType);
layers[ordinal] = armType;
dirty |= (1 << ordinal);
}
protected byte getTransform(Component component) {
return (byte) 0xFF;
}
public Vector3 position() {
return position;
}
public Vector3 velocity() {
return velocity;
}
public float getAngle() {
return angle;
}
public void setAngle(float rad) {
if (angle != rad) {
angle = rad;
if (animation != null) animation.setDirection(getDirection());
}
}
public int getDirection() {
int numDirs = animation.getNumDirections();
return Direction.radiansToDirection(angle, numDirs);
}
public final void invalidate() {
dirty = Dirty.ALL;
}
public final void validate() {
if (dirty == 0) {
return;
}
update();
}
@CallSuper
protected void update() {
String path = getCOF();
//Gdx.app.debug(TAG, path);
COF cof = getCOFs().lookup(path);
if (DEBUG_COF) Gdx.app.debug(TAG, "" + cof);
boolean changed = updateAnimation(cof);
if (changed) {
dirty = Dirty.ALL;
animation.setDirection(getDirection());
}
if (DEBUG_DIRTY) Gdx.app.debug(TAG, "dirty layers: " + dirty);
for (int l = 0; l < cof.getNumLayers(); l++) {
COF.Layer layer = cof.getLayer(l);
final int c = layer.component;
if (!Dirty.isDirty(dirty, c)) {
continue;
}
final Component comp = Component.valueOf(c);
if (comp == null) continue;
String component = comp.name();
String armType = layers[c];
String weaponClass = layer.weaponClass;
path = entType.PATH + type + "\\" + component + "\\" + type + component + armType + mode + weaponClass + ".dcc";
if (armType.isEmpty()) {
animation.setLayer(c, null);
continue;
}
Gdx.app.log(TAG, path);
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);
byte transform = getTransform(comp);
animation.getLayer(c).setTransform(transform);
/*
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;
}
private boolean updateAnimation(COF cof) {
if (animation == null) {
animation = Animation.newAnimation(cof);
return true;
} else {
return animation.reset(cof);
}
}
public String getCOF() {
return type + mode + weaponClass;
}
protected COFD2 getCOFs() {
return Diablo.cofs.active;
}
public void drawDebug(ShapeRenderer shapes) {
float x = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50);
float y = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50);
final float R = 32;
shapes.setColor(Color.RED);
shapes.line(x, y, x + MathUtils.cos(angle) * R, y + MathUtils.sin(angle) * R);
// FIXME: Should be number of direction dependent, not 16, one of 4,8,16,32
float rounded = Direction.radiansToDirection16Radians(angle);
shapes.setColor(Color.GREEN);
shapes.line(x, y, x + MathUtils.cos(rounded) * R * 0.5f, y + MathUtils.sin(rounded) * R * 0.5f);
}
public void draw(Batch batch) {
draw((PaletteIndexedBatch) batch);
}
public void draw(PaletteIndexedBatch batch) {
validate();
animation.act();
float x = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50);
float y = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50);
animation.draw(batch, x, y);
}
public boolean move() {
int x = Direction.getOffX(angle);
int y = Direction.getOffY(angle);
position.add(x, y, 0);
return true;
}
}

View File

@ -3,14 +3,10 @@ package gdx.diablo.entity;
import com.google.common.base.Preconditions;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.ObjectUtils;
import java.util.EnumMap;
import java.util.Map;
@ -19,154 +15,305 @@ import java.util.concurrent.CopyOnWriteArraySet;
import gdx.diablo.CharClass;
import gdx.diablo.Diablo;
import gdx.diablo.codec.Animation;
import gdx.diablo.codec.COF;
import gdx.diablo.ItemCodes;
import gdx.diablo.codec.COFD2;
import gdx.diablo.codec.D2S;
import gdx.diablo.codec.DCC;
import gdx.diablo.codec.excel.Armor;
import gdx.diablo.codec.excel.ItemEntry;
import gdx.diablo.codec.excel.PlrMode;
import gdx.diablo.codec.excel.PlrType;
import gdx.diablo.codec.excel.WeaponClass;
import gdx.diablo.codec.excel.Weapons;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.item.BodyLoc;
import gdx.diablo.item.Item;
import gdx.diablo.server.Connect;
public class Player {
public class Player extends Entity {
private static final String TAG = "Player";
private static final boolean DEBUG = true;
private static final boolean DEBUG_COF = DEBUG && !true;
private static final boolean DEBUG_EQUIPPED = DEBUG && true;
private static final boolean DEBUG_INVENTORY = DEBUG && true;
private static final String CHARS = "data\\global\\chars\\";
private static final boolean DEBUG = true;
private static final boolean DEBUG_STATE = DEBUG && true;
public static final int MAX_NAME_LENGTH = 15;
D2S d2s;
String name;
GridPoint2 origin;
float angle;
public enum Slot {
HEAD, NECK, TORS, RARM, LARM, RRIN, LRIN, BELT, FEET, GLOV;
int plrModeId;
PlrMode.Entry plrMode;
public BodyLoc toBodyLoc(boolean alternate) {
return toBodyLoc(this, alternate);
}
int plrTypeId;
PlrType.Entry plrType;
public static BodyLoc toBodyLoc(Slot slot, boolean alternate) {
switch (slot) {
case HEAD: return BodyLoc.HEAD;
case NECK: return BodyLoc.NECK;
case TORS: return BodyLoc.TORS;
case RARM: return alternate ? BodyLoc.RARM2 : BodyLoc.RARM;
case LARM: return alternate ? BodyLoc.LARM2 : BodyLoc.LARM;
case RRIN: return BodyLoc.RRIN;
case LRIN: return BodyLoc.LRIN;
case BELT: return BodyLoc.BELT;
case FEET: return BodyLoc.FEET;
case GLOV: return BodyLoc.GLOV;
default:
throw new GdxRuntimeException("Invalid slot: " + slot);
}
}
}
//int weaponClassId;
WeaponClass.Entry weaponClass;
boolean dirty;
String cofId;
Animation anim;
EnumMap<BodyLoc, Item> equipped;
Array<Item> inventory;
boolean usingAlternate;
boolean alternate;
boolean ignoreUpdate;
byte[] transforms;
EnumMap<BodyLoc, Item> equipped = new EnumMap<>(BodyLoc.class);
Array<Item> inventory = new Array<>();
public Stats stats;
final Set<SlotListener> SLOT_LISTENERS = new CopyOnWriteArraySet<>();
public Player(D2S d2s) {
this.d2s = d2s;
name = d2s.name;
plrTypeId = d2s.charClass;
plrType = Diablo.files.PlrType.get(plrTypeId);
origin = new GridPoint2();
init();
//setWeaponClass("hth");
public Player(String name, CharClass clazz) {
this(name, clazz.id);
}
equipped = d2s.items.equipped;
public Player(D2S d2s) {
super(Diablo.files.PlrType.get(d2s.charClass).Token, EntType.PLAYER);
setMode("TN");
stats = new D2SStats(d2s);
loadEquipped(d2s.items.equipped);
loadInventory(d2s.items.inventory);
}
public Player(String name, int classId) {
super(Diablo.files.PlrType.get(classId).Token, EntType.PLAYER);
setMode("TN");
stats = new StatsImpl(name, classId);
}
public Player(Connect connect) {
this(connect.name, connect.classId);
ignoreUpdate = true;
transforms = connect.colors;
setWeaponClass("1HS");
for (int i = 0; i < 16; i++) {
String code = ItemCodes.getCode(connect.composites[i] & 0xFF);
if (code == null) code = ItemCodes.getCode(ItemCodes.LIT);
setArmType(Component.valueOf(i), code);
}
}
private void loadEquipped(EnumMap<BodyLoc, Item> items) {
equipped.putAll(items);
for (Map.Entry<BodyLoc, Item> entry : equipped.entrySet()) {
entry.getValue().load();
if (DEBUG_EQUIPPED) Gdx.app.debug(TAG, entry.getKey() + ": " + entry.getValue());
//if (DEBUG_EQUIPPED) Gdx.app.debug(TAG, entry.getKey() + ": " + entry.getValue());
}
}
inventory = d2s.items.inventory;
for (Item item : inventory) {
private void loadInventory(Array<Item> items) {
inventory.addAll(items);
for (Item item : items) {
item.load();
if (DEBUG_INVENTORY) Gdx.app.debug(TAG, item.gridX + "," + item.gridY + ": " + item);
//if (DEBUG_INVENTORY) Gdx.app.debug(TAG, item.gridX + "," + item.gridY + ": " + item);
}
}
public Player(String name, CharClass clazz) {
this.name = name;
plrTypeId = clazz.id;
plrType = Diablo.files.PlrType.get(plrTypeId);
origin = new GridPoint2();
init();
//setWeaponClass("hth");
dirty = true;
public Item getSlot(Slot slot) {
BodyLoc loc = slot.toBodyLoc(alternate);
return getSlot(loc);
}
private void init() {
setMode("TN");
setAngle(MathUtils.PI * 3 / 2);
public Item getSlot(BodyLoc loc) {
return equipped.get(loc);
}
public int getClassId() {
return d2s.charClass;
public Item setSlot(Slot slot, Item item) {
Preconditions.checkState(item == null || getSlot(slot) == null, "Slot must be empty first!");
BodyLoc loc = slot.toBodyLoc(alternate);
return setSlot(loc, item);
}
public CharClass getCharClass() {
return CharClass.get(d2s.charClass);
}
public Item setSlot(BodyLoc loc, Item item) {
Item oldItem = equipped.put(loc, item);
public String getName() {
return d2s.name;
}
//invalidate();
//setArmType(slot, item.base.alternateGfx);
int components = loc.components();
if (components > 0) dirty |= components;
updateWeaponClass();
public int getLevel() {
return d2s.stats.level;
}
public long getExperience() {
return d2s.stats.xp;
}
public int getStrength() {
return d2s.stats.strength;
}
public int getDexterity() {
return d2s.stats.dexterity;
}
public int getVitality() {
return d2s.stats.vitality;
}
public int getEnergy() {
return d2s.stats.energy;
}
public int getFireResistance() {
return 0;
}
public int getColdResistance() {
return 0;
}
public int getLightningResistance() {
return 0;
}
public int getPoisonResistance() {
return 0;
}
public Item getBodyLoc(BodyLoc bodyLoc) {
return equipped.get(bodyLoc);
}
public Item setBodyLoc(BodyLoc bodyLoc, Item item) {
Preconditions.checkState(item == null || getBodyLoc(bodyLoc) == null, "Slot must be empty first!");
Item oldItem = equipped.put(bodyLoc, item);
for (SlotListener l : SLOT_LISTENERS) l.onChanged(this, bodyLoc, oldItem, item);
notifySlotChanged(loc, oldItem, item);
return oldItem;
}
@Override
protected byte getTransform(Component component) {
if (ignoreUpdate) {
return transforms[component.ordinal()];
}
switch (component) {
case HD: return packTransform(Slot.HEAD);
case TR:
case RA:
case LA:
case S1:
case S2: return packTransform(Slot.TORS);
// TODO: Shield/weapons?
default: return super.getTransform(component);
}
}
private byte packTransform(Slot slot) {
Item item = getSlot(slot);
if (item == null) return super.getTransform(null);
return (byte) ((item.base.Transform << 5) | (item.charColorIndex & 0x1F));
}
public Array<Item> getInventory() {
return inventory;
}
public boolean isAlternate() {
return alternate;
}
public void setAlternate(boolean b) {
if (alternate != b) {
alternate = b;
updateWeaponClass();
Item LH = getSlot(BodyLoc.LARM);
Item RH = getSlot(BodyLoc.RARM);
Item LH2 = getSlot(BodyLoc.LARM2);
Item RH2 = getSlot(BodyLoc.RARM2);
if (b) {
notifyAlternate(LH2, RH2);
} else {
notifyAlternate(LH, RH);
}
}
}
@Override
protected COFD2 getCOFs() {
return Diablo.cofs.chars_cof;
}
public void update() {
if (ignoreUpdate) {
super.update();
return;
}
updateWeaponClass();
Item head = getSlot(Slot.HEAD);
setArmType(Component.HD, head != null ? head.base.alternateGfx : "LIT");
Item body = getSlot(Slot.TORS);
if (body != null) {
Armor.Entry armor = body.getBase();
setArmType(Component.TR, Diablo.files.ArmType.get(armor.Torso).Token);
setArmType(Component.LG, Diablo.files.ArmType.get(armor.Legs ).Token);
setArmType(Component.RA, Diablo.files.ArmType.get(armor.rArm ).Token);
setArmType(Component.LA, Diablo.files.ArmType.get(armor.lArm ).Token);
setArmType(Component.S1, Diablo.files.ArmType.get(armor.lSPad).Token);
setArmType(Component.S2, Diablo.files.ArmType.get(armor.rSPad).Token);
} else {
setArmType(Component.TR, DEFAULT_LAYER);
setArmType(Component.LG, DEFAULT_LAYER);
setArmType(Component.RA, DEFAULT_LAYER);
setArmType(Component.LA, DEFAULT_LAYER);
setArmType(Component.S1, DEFAULT_LAYER);
setArmType(Component.S2, DEFAULT_LAYER);
}
super.update();
}
private void updateWeaponClass() {
Item RH = null, LH = null, SH = null;
Item rArm = getSlot(Slot.RARM);
if (rArm != null) {
if (rArm.type.is("weap")) {
RH = rArm;
} else if (rArm.type.is("shld")) {
SH = rArm;
}
}
Item lArm = getSlot(Slot.LARM);
if (lArm != null) {
if (lArm.type.is("weap")) {
LH = lArm;
} else if (lArm.type.is("shld")) {
SH = lArm;
}
}
if (DEBUG_STATE) {
Gdx.app.debug(TAG, "RH = " + RH);
Gdx.app.debug(TAG, "LH = " + LH);
Gdx.app.debug(TAG, "SH = " + SH);
}
if (LH != null && RH != null) {
Weapons.Entry LHEntry = LH.getBase();
Weapons.Entry RHEntry = RH.getBase();
if ( LHEntry.wclass.equals("1hs") && RHEntry.wclass.equals("1hs")) {
setWeaponClass("1SS"); // Left Swing Right Swing
} else if (LHEntry.wclass.equals("1hs") && RHEntry.wclass.equals("1ht")) {
setWeaponClass("1ST"); // Left Swing Right Thrust
} else if (LHEntry.wclass.equals("1ht") && RHEntry.wclass.equals("1hs")) {
setWeaponClass("1JS"); // Left Jab Right Swing
} else if (LHEntry.wclass.equals("1ht") && RHEntry.wclass.equals("1ht")) {
setWeaponClass("1JT"); // Left Jab Right Thrust
} else if (LH.type.is("miss") || RH.type.is("miss")) {
setWeaponClass(LH.type.is("miss") ? LHEntry.wclass : RHEntry.wclass);
} else if (LH.type.is("h2h") || RH.type.is("h2h")) {
setWeaponClass("HT2"); // Two Hand-to-Hand
} else {
setWeaponClass("HTH");
Gdx.app.error(TAG, String.format(
"Unknown weapon combination: LH=%s RH=%s", LHEntry.wclass, RHEntry.wclass));
}
} else if (LH != null || RH != null) {
RH = ObjectUtils.firstNonNull(RH, LH);
LH = null;
if (RH.type.is("bow")) {
LH = RH;
RH = null;
Weapons.Entry LHEntry = LH.getBase();
setWeaponClass(LHEntry.wclass);
} else if (RH.type.is("weap")) { // make sure weap and not e.g. misl, might not be required
Weapons.Entry RHEntry = RH.getBase();
setWeaponClass(RHEntry.wclass);
} else {
setWeaponClass("HTH");
}
} else {
setWeaponClass("HTH");
}
setArmType(Component.RH, RH != null ? RH.base.alternateGfx : "");
setArmType(Component.LH, LH != null ? LH.base.alternateGfx : "");
setArmType(Component.SH, SH != null ? SH.base.alternateGfx : "");
}
@Override
public boolean move() {
if (!mode.equalsIgnoreCase("WL")
&& !mode.equalsIgnoreCase("RN")
&& !mode.equalsIgnoreCase("TW")) {
return false;
}
return super.move();
}
private void notifySlotChanged(BodyLoc bodyLoc, Item oldItem, Item item) {
for (SlotListener l : SLOT_LISTENERS) l.onChanged(this, bodyLoc, oldItem, item);
}
private void notifyAlternate(Item LH, Item RH) {
for (SlotListener l : SLOT_LISTENERS) l.onAlternate(this, LH, RH);
}
public boolean addSlotListener(SlotListener l) {
boolean added = SLOT_LISTENERS.add(l);
return added;
@ -186,218 +333,165 @@ public class Player {
return !empty;
}
public Item getComponentSlot(int component) {
switch (component) {
case COF.Component.HD: return getBodyLoc(BodyLoc.HEAD);
case COF.Component.TR:
case COF.Component.RA:
case COF.Component.LA:
case COF.Component.S1:
case COF.Component.S2: return getBodyLoc(BodyLoc.TORS);
// TODO: Shield/weapons?
default: return null;
}
}
public Array<Item> getInventory() {
return inventory;
}
public boolean isAlternate() {
return usingAlternate;
}
public void setAlternate(boolean b) {
if (usingAlternate != b) {
usingAlternate = b;
}
}
public static Player obtain(D2S d2s) {
return new Player(d2s);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.append("name", name)
.append("origin", origin)
.append("plrMode", plrMode)
.append("plrType", plrType)
.append("weaponClass", weaponClass)
.build();
}
public void setMode(String code) {
setMode(Diablo.files.PlrMode.index(code));
}
public void setMode(int plrModeId) {
if (this.plrModeId != plrModeId || plrMode == null) {
this.plrModeId = plrModeId;
plrMode = Diablo.files.PlrMode.get(plrModeId);
dirty = true;
}
}
/*
public void setWeaponClass(String code) {
setWeaponClass(Diablo.files.WeaponClass.index(code));
}
public void setWeaponClass(int weaponClassId) {
if (this.weaponClassId != weaponClassId || weaponClass == null) {
this.weaponClassId = weaponClassId;
weaponClass = Diablo.files.WeaponClass.get(weaponClassId);
dirty = true;
}
}
*/
public void setAngle(float rad) {
if (this.angle != rad) {
this.angle = rad;
if (anim != null) anim.setDirection(getDirection());
}
}
public int getDirection() {
return Direction.radiansToDirection(angle, 16);
}
public GridPoint2 getOrigin() {
return origin;
}
public void update() {
if (!dirty) {
return;
}
dirty = false;
String[] components = new String[COF.Component.NUM_COMPONENTS];
Item rHand, lHand;
if (!usingAlternate) {
rHand = getBodyLoc(BodyLoc.RARM);
lHand = getBodyLoc(BodyLoc.LARM);
} else {
rHand = getBodyLoc(BodyLoc.RARM2);
lHand = getBodyLoc(BodyLoc.LARM2);
}
// TODO: custom code for barbarian _1or2handed
if (rHand != null) {
ItemEntry entry = rHand.base;
components[entry.component] = entry.alternateGfx;
if (entry instanceof Weapons.Entry) {
weaponClass = Diablo.files.WeaponClass.get(((Weapons.Entry) entry).wclass);
}
}
if (lHand != null) {
ItemEntry entry = lHand.base;
components[entry.component] = entry.alternateGfx;
if (entry instanceof Weapons.Entry) {
weaponClass = Diablo.files.WeaponClass.get(((Weapons.Entry) entry).wclass);
}
}
if (weaponClass == null) {
weaponClass = Diablo.files.WeaponClass.get("hth");
}
Item head = getBodyLoc(BodyLoc.HEAD);
components[COF.Component.HD] = head != null ? head.base.alternateGfx : null;
Item body = getBodyLoc(BodyLoc.TORS);
if (body != null) {
Armor.Entry armor = body.getBase();
components[COF.Component.TR] = Diablo.files.ArmType.get(armor.Torso).Token;
components[COF.Component.LG] = Diablo.files.ArmType.get(armor.Legs).Token;
components[COF.Component.RA] = Diablo.files.ArmType.get(armor.rArm).Token;
components[COF.Component.LA] = Diablo.files.ArmType.get(armor.lArm).Token;
components[COF.Component.S1] = Diablo.files.ArmType.get(armor.lSPad).Token;
components[COF.Component.S2] = Diablo.files.ArmType.get(armor.rSPad).Token;
} else {
components[COF.Component.TR] =
components[COF.Component.LG] =
components[COF.Component.RA] =
components[COF.Component.LA] =
components[COF.Component.S1] =
components[COF.Component.S2] = "lit";
}
String cofId = plrType.Token + plrMode.Token + weaponClass.Code;
if (DEBUG_COF) Gdx.app.debug(TAG, "COF: " + this.cofId + " -> " + cofId);
COF cof = Diablo.cofs.chars_cof.lookup(cofId);
this.cofId = cofId;
// FIXME: dispose/unload old animation layer
//if (animation != null) animation.dispose();
Animation oldAnim = anim;
anim = Animation.newAnimation(cof);
// TODO: This might be a problem
anim.setDirection(oldAnim != null ? oldAnim.getDirection() : getDirection());
for (int i = 0; i < cof.getNumLayers(); i++) {
COF.Layer layer = cof.getLayer(i);
String component = Diablo.files.Composit.get(layer.component).Token;
String armorClass = components[layer.component];
if (armorClass == null) continue;
String weaponClass = layer.weaponClass;
String path = CHARS + plrType.Token + "\\" + component + "\\" + plrType.Token + component + armorClass + plrMode.Token + weaponClass + ".dcc";
AssetDescriptor<DCC> descriptor = new AssetDescriptor<>(path, DCC.class);
Diablo.assets.load(descriptor);
Diablo.assets.finishLoadingAsset(descriptor);
DCC dcc = Diablo.assets.get(descriptor);
anim.setLayer(layer.component, dcc);
Item item = getComponentSlot(layer.component);
if (item != null) {
anim.getLayer(layer.component).setTransform(item.charColormap, item.charColorIndex);
/*
int trans = item.charTransformation;
if (trans != 0xFFFFFFFF) {
anim.getLayer(layer.component).setTransform(trans >>> 8);
anim.getLayer(layer.component).setTransformColor(trans & 0xFF);
}
*/
}
}
}
public void move() {
switch (plrModeId) {
case 2: case 3: case 6:
break;
default:
return;
}
int x = Direction.getOffX(angle);
int y = Direction.getOffY(angle);
origin.add(x, y);
}
public void draw(PaletteIndexedBatch batch, int x, int y) {
update();
anim.act();
anim.draw(batch, x, y);
}
public void drawDebug(ShapeRenderer shapes, int x, int y) {
final float R = 32;
shapes.setColor(Color.RED);
shapes.line(x, y, x + MathUtils.cos(angle) * R, y + MathUtils.sin(angle) * R);
float rounded = Direction.radiansToDirection16Radians(angle);
shapes.setColor(Color.GREEN);
shapes.line(x, y, x + MathUtils.cos(rounded) * R * 0.5f, y + MathUtils.sin(rounded) * R * 0.5f);
}
public interface SlotListener {
void onChanged(Player player, BodyLoc bodyLoc, Item oldItem, Item item);
void onAlternate(Player player, Item LH, Item RH);
}
public static class SlotAdapter implements SlotListener {
@Override public void onChanged(Player player, BodyLoc bodyLoc, Item oldItem, Item item) {}
@Override public void onAlternate(Player player, Item LH, Item RH) {}
}
public interface Stats {
int getClassId();
CharClass getCharClass();
String getName();
int getLevel();
long getExperience();
int getStrength();
int getDexterity();
int getVitality();
int getEnergy();
int getFireResistance();
int getColdResistance();
int getLightningResistance();
int getPoisonResistance();
}
public class StatsImpl implements Stats {
final String name;
final int classId;
StatsImpl(String name, int classId) {
this.name = name;
this.classId = classId;
}
@Override
public int getClassId() {
return classId;
}
@Override
public CharClass getCharClass() {
return CharClass.get(getClassId());
}
@Override
public String getName() {
return name;
}
@Override
public int getLevel() {
return 0;
}
@Override
public long getExperience() {
return 0;
}
@Override
public int getStrength() {
return 0;
}
@Override
public int getDexterity() {
return 0;
}
@Override
public int getVitality() {
return 0;
}
@Override
public int getEnergy() {
return 0;
}
@Override
public int getFireResistance() {
return 0;
}
@Override
public int getColdResistance() {
return 0;
}
@Override
public int getLightningResistance() {
return 0;
}
@Override
public int getPoisonResistance() {
return 0;
}
}
public class D2SStats implements Stats {
public final D2S d2s;
D2SStats(D2S d2s) {
this.d2s = d2s;
}
@Override
public int getClassId() {
return d2s.charClass;
}
@Override
public CharClass getCharClass() {
return CharClass.get(getClassId());
}
@Override
public String getName() {
return d2s.name;
}
public int getLevel() {
return d2s.stats.level;
}
public long getExperience() {
return d2s.stats.xp;
}
public int getStrength() {
return d2s.stats.strength;
}
public int getDexterity() {
return d2s.stats.dexterity;
}
public int getVitality() {
return d2s.stats.vitality;
}
public int getEnergy() {
return d2s.stats.energy;
}
public int getFireResistance() {
return 0;
}
public int getColdResistance() {
return 0;
}
public int getLightningResistance() {
return 0;
}
public int getPoisonResistance() {
return 0;
}
}
}

View File

@ -363,6 +363,7 @@ public class Map implements Disposable {
public GridPoint2 find(int id) {
GridPoint2 origin = zones.first().presets[0][0].ds1.find(id);
if (origin == null) return null;
origin = origin.cpy();
origin.x *= DT1.Tile.SUBTILE_SIZE;
origin.y *= DT1.Tile.SUBTILE_SIZE;
return origin.add(DT1.Tile.SUBTILE_CENTER);

View File

@ -16,7 +16,7 @@ import java.text.NumberFormat;
import gdx.diablo.Cvars;
import gdx.diablo.Diablo;
import gdx.diablo.codec.DC6;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.screen.GameScreen;
import gdx.diablo.widget.Button;

View File

@ -22,7 +22,7 @@ import gdx.diablo.codec.DC6;
import gdx.diablo.codec.excel.BodyLocs;
import gdx.diablo.codec.excel.Inventory;
import gdx.diablo.codec.util.BBox;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.item.BodyLoc;
import gdx.diablo.item.Item;
@ -309,7 +309,7 @@ public class InventoryPanel extends WidgetGroup implements Disposable {
Item cursor = Diablo.cursor.getItem();
if (cursor != null) {
if (!ArrayUtils.contains(cursor.type.BodyLoc, bodyPart)) {
Diablo.audio.play("sorceress_impossible_1", false);
Diablo.audio.play(gameScreen.player.stats.getCharClass().name().toLowerCase() + "_impossible_1", false);
return;
}

View File

@ -26,7 +26,7 @@ import gdx.diablo.CharClass;
import gdx.diablo.Diablo;
import gdx.diablo.codec.Animation;
import gdx.diablo.codec.DC6;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.widget.CharButton;

View File

@ -22,7 +22,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.scenes.scene2d.utils.UIUtils;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.Timer;
import org.apache.commons.io.IOUtils;
@ -34,7 +34,7 @@ import java.io.PrintWriter;
import gdx.diablo.Diablo;
import gdx.diablo.Keys;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.graphics.PaletteIndexedColorDrawable;
import gdx.diablo.key.MappedKey;
@ -49,6 +49,7 @@ import gdx.diablo.panel.InventoryPanel;
import gdx.diablo.panel.MobilePanel;
import gdx.diablo.panel.StashPanel;
import gdx.diablo.server.Connect;
import gdx.diablo.server.ConnectResponse;
import gdx.diablo.server.Disconnect;
import gdx.diablo.server.Message;
import gdx.diablo.server.MoveTo;
@ -92,7 +93,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
//Char character;
public Player player;
ObjectMap<String, Player> otherPlayers = new ObjectMap<>();
IntMap<Player> entities = new IntMap<>();
Timer.Task updateTask;
Socket socket;
@ -132,7 +133,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
this.fontColor = Diablo.colors.white;
this.cursor = new TextureRegionDrawable(Diablo.textures.white);
}});
output.setDebug(true);
//output.setDebug(true);
output.setSize(Diablo.VIRTUAL_WIDTH * 0.75f, Diablo.fonts.fontformal12.getLineHeight() * 8);
output.setPosition(10, Diablo.VIRTUAL_HEIGHT - 10, Align.topLeft);
output.setAlignment(Align.topLeft);
@ -321,20 +322,30 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
Connect connect = packet.readValue(Connect.class);
output.appendText(Diablo.string.format(3641, connect.name));
output.appendText("\n");
Player q = player.clone();
q.setOrigin(player.origin().cpy());
otherPlayers.put(connect.name, q);
// FIXME: Default position is in subtiles? Divide 5 temp fix
Player connector = new Player(connect);
GridPoint2 startPos = map.find(Map.ID.TOWN_ENTRY_1);
connector.position().set(startPos.x, startPos.y, 0);
entities.put(connect.id, connector);
break;
case Packets.DISCONNECT:
Disconnect disconnect = packet.readValue(Disconnect.class);
output.appendText(Diablo.string.format(3642, disconnect.name));
output.appendText("\n");
otherPlayers.remove(disconnect.name);
entities.remove(disconnect.id);
break;
case Packets.MOVETO:
MoveTo moveTo = packet.readValue(MoveTo.class);
Player p = otherPlayers.get(moveTo.name);
if (p != null) p.origin().set(moveTo.x, moveTo.y);
Player p = entities.get(moveTo.id);
if (p != null) {
p.position().set(moveTo.x, moveTo.y, 0);
p.setAngle(moveTo.angle);
}
break;
case Packets.CONNECT_RESPONSE:
ConnectResponse connectResponse = packet.readValue(ConnectResponse.class);
entities.put(connectResponse.id, player);
break;
}
}
@ -357,9 +368,9 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
//int spx = + (player.getOrigin().x * Tile.SUBTILE_WIDTH50) - (player.getOrigin().y * Tile.SUBTILE_WIDTH50);
//int spy = - (player.getOrigin().x * Tile.SUBTILE_HEIGHT50) - (player.getOrigin().y * Tile.SUBTILE_HEIGHT50);
//player.draw(b, spx, spy);
player.draw(b);
//player.draw(b);
for (Player p : otherPlayers.values()) {
for (Player p : entities.values()) {
p.draw(b);
}
@ -398,8 +409,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
//character.x = origin.x;
//character.y = origin.y;
player.origin().set(origin);
Gdx.app.debug(TAG, player.toString());
player.position().set(origin.x, origin.y, 0);
Keys.Esc.addStateListener(mappedKeyStateListener);
Keys.Inventory.addStateListener(mappedKeyStateListener);
@ -414,15 +424,22 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
Gdx.app.log(TAG, "connecting to " + socket.getRemoteAddress() + "...");
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String connect = Packets.build(new Connect(player));
out.println(connect);
}
updateTask = Timer.schedule(new Timer.Task() {
GridPoint2 position = new GridPoint2();
@Override
public void run() {
if (UIUtils.shift()) return;
player.move();
mapRenderer.setPosition(player.origin());
String moveTo = Packets.build(new MoveTo(player.stats.getName(), player.origin()));
boolean moved = player.move();
position.set((int) player.position().x, (int) player.position().y);
mapRenderer.setPosition(position);
if (!moved) return;
String moveTo = Packets.build(new MoveTo(position, player.getAngle()));
out.println(moveTo);
}
}, 0, 1 / 25f);
@ -463,7 +480,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable
@Override
public void pause() {
escapePanel.setVisible(true);
//escapePanel.setVisible(true);
}
@Override

View File

@ -39,7 +39,7 @@ import java.net.SocketTimeoutException;
import gdx.diablo.Diablo;
import gdx.diablo.codec.DC6;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.server.Account;
@ -280,7 +280,7 @@ public class LobbyScreen extends ScreenAdapter {
Net.HttpRequest request = new HttpRequestBuilder()
.newRequest()
.method(Net.HttpMethods.POST)
.url("http://hydra:6112/create-session")
.url("http://" + Diablo.client.getRealm() + ":6112/create-session")
.jsonContent(new Session.Builder() {{
name = tfGameName.getText();
password = tfPassword.getText();
@ -462,7 +462,7 @@ public class LobbyScreen extends ScreenAdapter {
Net.HttpRequest request = new HttpRequestBuilder()
.newRequest()
.method(Net.HttpMethods.GET)
.url("http://hydra:6112/get-sessions")
.url("http://" + Diablo.client.getRealm() + ":6112/get-sessions")
.build();
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
@Override
@ -528,7 +528,7 @@ public class LobbyScreen extends ScreenAdapter {
@Override
public void run() {
try {
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, "hydra", 6113, new SocketHints());
socket = Gdx.net.newClientSocket(Net.Protocol.TCP, Diablo.client.getRealm(), 6113, new SocketHints());
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
} catch (Throwable t) {

View File

@ -115,7 +115,7 @@ public class LoginScreen extends ScreenAdapter {
Net.HttpRequest request = new HttpRequestBuilder()
.newRequest()
.method(Net.HttpMethods.POST)
.url("http://hydra:6112/login")
.url("http://" + Diablo.client.getRealm() + ":6112/login")
.jsonContent(new Account.Builder() {{ account = "test"; }})
.build();
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {

View File

@ -17,7 +17,7 @@ import com.badlogic.gdx.utils.Array;
import gdx.diablo.Diablo;
import gdx.diablo.codec.D2S;
import gdx.diablo.codec.DC6;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.widget.SelectButton;

View File

@ -17,7 +17,7 @@ import com.badlogic.gdx.utils.Array;
import gdx.diablo.Diablo;
import gdx.diablo.codec.D2S;
import gdx.diablo.codec.DC6;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.loader.DC6Loader;
import gdx.diablo.server.Account;

View File

@ -1,13 +1,29 @@
package gdx.diablo.server;
import gdx.diablo.entity.Player;
public class Connect {
public int id;
public String name;
public int classId;
public byte[] composites;
public byte[] colors;
private Connect() {}
public Connect(String name) {
this.name = name;
public Connect(String name, int classId, byte[] composites, byte[] colors) {
this.name = name;
this.classId = classId;
this.composites = composites;
this.colors = colors;
}
public Connect(Player player) {
Player.D2SStats stats = (Player.D2SStats) player.stats;
this.name = stats.d2s.name;
this.classId = stats.d2s.charClass;
this.composites = stats.d2s.composites;
this.colors = stats.d2s.colors;
}
}

View File

@ -0,0 +1,13 @@
package gdx.diablo.server;
public class ConnectResponse {
public int id;
private ConnectResponse() {}
public ConnectResponse(int id) {
this.id = id;
}
}

View File

@ -2,11 +2,13 @@ package gdx.diablo.server;
public class Disconnect {
public int id;
public String name;
private Disconnect() {}
public Disconnect(String name) {
public Disconnect(int id, String name) {
this.id = id;
this.name = name;
}

View File

@ -0,0 +1,16 @@
package gdx.diablo.server;
import gdx.diablo.Diablo;
public class Event {
public int id;
public String[] args;
private Event() {}
@Override
public String toString() {
return Diablo.string.format(id, (Object[]) args);
}
}

View File

@ -4,16 +4,17 @@ import com.badlogic.gdx.math.GridPoint2;
public class MoveTo {
public String name;
public int x;
public int y;
public int id;
public int x;
public int y;
public float angle;
private MoveTo() {}
public MoveTo(String name, GridPoint2 origin) {
this.name = name;
public MoveTo(GridPoint2 origin, float angle) {
x = origin.x;
y = origin.y;
this.angle = angle;
}
}

View File

@ -15,14 +15,16 @@ public class Packets {
public static final int CONNECT = 2;
public static final int DISCONNECT = 3;
public static final int MOVETO = 4;
public static final int CONNECT_RESPONSE = 5;
private static final ObjectIntMap<Class> MAP;
static {
MAP = new ObjectIntMap<>();
MAP.put(Message.class, 1);
MAP.put(Connect.class, 2);
MAP.put(Disconnect.class, 3);
MAP.put(MoveTo.class, 4);
MAP.put(Message.class, 1);
MAP.put(Connect.class, 2);
MAP.put(Disconnect.class, 3);
MAP.put(MoveTo.class, 4);
MAP.put(ConnectResponse.class, 5);
}
public static <T> T parse(Class<T> type, String json) {

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.net.ServerSocket;
import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.JsonReader;
@ -18,6 +19,8 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import gdx.diablo.entity.Entity;
public class Server implements Disposable, Runnable {
private static final String TAG = "Server";
@ -30,6 +33,7 @@ public class Server implements Disposable, Runnable {
Thread connectionListener;
int port;
String name;
IntMap<Entity> entities = new IntMap<>(); // TODO: synchronize
public Server(int port) {
this(port, "");
@ -52,7 +56,7 @@ public class Server implements Disposable, Runnable {
while (!kill.get()) {
try {
Socket socket = server.accept(null);
new Client(socket, "Tirant").start();
new Client(socket).start();
Gdx.app.log(name, "connection from " + socket.getRemoteAddress());
} catch (Throwable t) {
Gdx.app.log(name, t.getMessage(), t);
@ -123,12 +127,13 @@ public class Server implements Disposable, Runnable {
Socket socket;
BufferedReader in;
PrintWriter out;
String name;
public Client(Socket socket, String name) {
int id;
Connect connect;
public Client(Socket socket) {
super(clientThreads, "Client-" + String.format("%08X", MathUtils.random(1, Integer.MAX_VALUE - 1)));
this.socket = socket;
this.name = name;
}
@Override
@ -137,7 +142,18 @@ public class Server implements Disposable, Runnable {
in = IOUtils.buffer(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String connect = Packets.build(new Connect(name));
connect = Packets.parse(Connect.class, in.readLine());
id = connect.id = entities.size + 1;
entities.put(id, null);
String connectResponse = Packets.build(new ConnectResponse(id));
out.println(connectResponse);
for (Client client : clients) {
out.println(Packets.build(client.connect));
}
String connect = Packets.build(this.connect);
Gdx.app.log(getName(), connect);
for (Client client : clients) {
client.out.println(connect);
//client.out.println("CONNECT " + socket.getRemoteAddress());
@ -160,6 +176,9 @@ public class Server implements Disposable, Runnable {
}
break;
case Packets.MOVETO:
MoveTo moveTo = packet.readValue(MoveTo.class);
moveTo.id = id;
input = Packets.build(moveTo);
for (Client client : clients) {
if (client == this) continue;
client.out.println(input);
@ -171,10 +190,11 @@ public class Server implements Disposable, Runnable {
} catch (Throwable t) {
Gdx.app.log(getName(), "ERROR " + socket.getRemoteAddress() + ": " + t.getMessage());
} finally {
entities.remove(id);
clients.remove(this);
String message = "DISCONNECT " + socket.getRemoteAddress();
Gdx.app.log(getName(), message);
String disconnect = Packets.build(new Disconnect(name));
String disconnect = Packets.build(new Disconnect(id, connect.name));
for (Client client : clients) {
client.out.println(disconnect);
}

View File

@ -101,7 +101,7 @@ public class BNetConnectDialog extends Dialog {
Net.HttpRequest request = new HttpRequestBuilder()
.newRequest()
.method(Net.HttpMethods.GET)
.url("http://hydra:6112/find-server")
.url("http://" + Diablo.client.getRealm() + ":6112/find-server")
.build();
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener() {
@Override

View File

@ -20,7 +20,7 @@ import gdx.diablo.BlendMode;
import gdx.diablo.Diablo;
import gdx.diablo.codec.excel.Inventory;
import gdx.diablo.codec.excel.ItemEntry;
import gdx.diablo.entity3.Player;
import gdx.diablo.entity.Player;
import gdx.diablo.graphics.PaletteIndexedBatch;
import gdx.diablo.item.Item;

View File

@ -13,9 +13,11 @@ import com.badlogic.gdx.utils.Json;
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.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.DateFormat;
import java.util.Calendar;
@ -31,6 +33,9 @@ public class ServerBrowser extends ApplicationAdapter {
new HeadlessApplication(new ServerBrowser(), config);
}
private static final boolean EXT_HOST = true;
private String host;
private final Json json = new Json();
private Map<String, Session> sessions = new ConcurrentHashMap<>();
@ -43,15 +48,39 @@ public class ServerBrowser extends ApplicationAdapter {
ServerBrowser() {}
private static String getIp() {
if (!EXT_HOST) {
try {
InetAddress address = InetAddress.getLocalHost();
return address.getHostAddress();
} catch (UnknownHostException e) {
Gdx.app.error(TAG, e.getMessage(), e);
return "hydra";
}
}
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new URL("http://checkip.amazonaws.com").openStream()));
return in.readLine();
} catch (IOException e) {
Gdx.app.error(TAG, e.getMessage(), e);
return "hydra";
} finally {
IOUtils.closeQuietly(in);
}
}
@Override
public void create() {
final Calendar calendar = Calendar.getInstance();
DateFormat format = DateFormat.getDateTimeInstance();
Gdx.app.log(TAG, format.format(calendar.getTime()));
host = getIp();
try {
InetAddress address = InetAddress.getLocalHost();
Gdx.app.log(TAG, "IP Address: " + address.getHostAddress());
Gdx.app.log(TAG, "IP Address: " + host);
Gdx.app.log(TAG, "Host Name: " + address.getHostName());
} catch (UnknownHostException e) {
Gdx.app.error(TAG, e.getMessage(), e);
@ -143,7 +172,7 @@ public class ServerBrowser extends ApplicationAdapter {
}
Session session = builder.build();
session.host = "hydra";
session.host = host;
session.port = 6114 + sessions.size();
sessions.put(session.getName(), session);