Complete revamp of the client-server protocol. It's now all binary in both directions,
authornotzed@gmail.com <notzed@gmail.com@b8b59bfb-1aa4-4687-8f88-a62eeb14c21e>
Sat, 2 Mar 2013 09:22:13 +0000 (09:22 +0000)
committernotzed@gmail.com <notzed@gmail.com@b8b59bfb-1aa4-4687-8f88-a62eeb14c21e>
Sat, 2 Mar 2013 09:22:13 +0000 (09:22 +0000)
and encoding and syntax is independent of semantics.
The protocol is now documented.

git-svn-id: file:///home/notzed/svn/duskz/trunk@8 b8b59bfb-1aa4-4687-8f88-a62eeb14c21e

32 files changed:
DuskCommon/src/duskz/protocol/DuskMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/DuskProtocol.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/EntityListMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/EntityUpdateMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/ListMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/MapMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/MessageType.java [deleted file]
DuskCommon/src/duskz/protocol/ServerMessage.java [deleted file]
DuskCommon/src/duskz/protocol/TransactionItem.java
DuskCommon/src/duskz/protocol/TransactionMessage.java [new file with mode: 0644]
DuskCommon/src/duskz/protocol/Wearing.java
DuskServer/src/duskz/server/BannedIPException.java [new file with mode: 0644]
DuskServer/src/duskz/server/Battle.java
DuskServer/src/duskz/server/BlockedIPException.java [new file with mode: 0644]
DuskServer/src/duskz/server/Commands.java
DuskServer/src/duskz/server/DuskEngine.java
DuskServer/src/duskz/server/Script.java
DuskServer/src/duskz/server/SpellGroup.java
DuskServer/src/duskz/server/entity/DuskObject.java
DuskServer/src/duskz/server/entity/Equipment.java
DuskServer/src/duskz/server/entity/Item.java
DuskServer/src/duskz/server/entity/LivingThing.java
DuskServer/src/duskz/server/entity/Merchant.java
DuskZ/src/duskz/client/ClientMap.java
DuskZ/src/duskz/client/Dusk.java
DuskZ/src/duskz/client/Entity.java
DuskZ/src/duskz/client/Equipment.java
DuskZ/src/duskz/client/GUI.java
DuskZ/src/duskz/client/Status.java
DuskZ/src/duskz/client/TransactionItem.java [deleted file]
DuskZ/src/duskz/client/fx/MainFrameFX.java
DuskZ/src/duskz/client/fx/TransactionPane.java

diff --git a/DuskCommon/src/duskz/protocol/DuskMessage.java b/DuskCommon/src/duskz/protocol/DuskMessage.java
new file mode 100644 (file)
index 0000000..dbf35be
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the base class and most of the implementation of a re-usable
+ * message encoding and decoding class.
+ *
+ * It encodes the data to a tagged binary format so that it may be decoded
+ * with no knowledge of the semantics of the content, and yet still provide
+ * a relatively efficient access.
+ *
+ * The semantics of the dusk protocol are stored in DuskProtocol.
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class DuskMessage {
+
+       // A list of any type of message including another list.
+       protected static final byte TC_LIST = 0;
+       // no args
+       protected static final byte TC_NOTIFY = 1;
+       // byte args
+       protected static final byte TC_BYTE = 2;
+       protected static final byte TC_INTEGER = 3;
+       protected static final byte TC_LONG = 4;
+       protected static final byte TC_FLOAT = 5;
+       protected static final byte TC_STRING = 6;
+       protected static final byte TC_STRING_LIST = 7;
+       // Generic messages targetted to a specific entity
+       protected static final byte TC_ENTITY_LIST = 8;
+       protected static final byte TC_ENTITY_NOTIFY = 9;
+       protected static final byte TC_ENTITY_BYTE = 10;
+       protected static final byte TC_ENTITY_INTEGER = 11;
+       protected static final byte TC_ENTITY_LONG = 12;
+       protected static final byte TC_ENTITY_FLOAT = 13;
+       protected static final byte TC_ENTITY_STRING = 14;
+       protected static final byte TC_ENTITY_STRING_LIST = 15;
+       // More specific messages for easier use and/or performance
+       // Should be used sparingly as changes are not hideable
+       // TODO: have a think about whether i really some of these as
+       // the overhead is only 2 bytes per field.
+       protected static final byte TC_MAP = 16;
+       protected static final byte TC_ENTITY_UPDATE = 17;
+       protected static final byte TC_TRANSACTION = 18;
+       /**
+        * The name is sent as a byte, allowing 256 different
+        * attributes per message type.
+        */
+       public int name;
+
+       public DuskMessage() {
+       }
+
+       public DuskMessage(int name) {
+               this.name = name;
+       }
+
+       public void send(DataOutputStream ostream) throws IOException {
+               ostream.writeByte(name);
+       }
+
+       public void receive(DataInputStream istream) throws IOException {
+               name = istream.readByte();
+       }
+
+       protected void format(PrintStream out, String s) {
+               out.printf("%s%s name=%d\n", s, getClass().getSimpleName(), name);
+       }
+
+       final public void format(PrintStream out) {
+               try {
+                       Class c = Class.forName("duskz.protocol.DuskProtocol");
+                       for (Field f : c.getDeclaredFields()) {
+                               if (f.getName().startsWith("MSG_")) {
+                                       if (f.getInt(c) == name) {
+                                               System.out.print(f.getName());
+                                               System.out.print(" ");
+                                       }
+                               }
+                       }
+               } catch (Exception x) {
+               }
+               format(out, "");
+       }
+
+       /**
+        * Must return a unique id from the field constants of DuskMessage which
+        * identifies the source object class.
+        *
+        * @return
+        */
+       public byte getType() {
+               return TC_NOTIFY;
+       }
+
+       static public DuskMessage createForType(int type) {
+               switch (type) {
+                       case TC_LIST:
+                               return new ListMessage();
+                       case TC_NOTIFY:
+                               return new DuskMessage();
+                       case TC_BYTE:
+                               return new DuskMessage.ByteMessage();
+                       case TC_INTEGER:
+                               return new DuskMessage.IntegerMessage();
+                       case TC_LONG:
+                               return new DuskMessage.LongMessage();
+                       case TC_FLOAT:
+                               return new DuskMessage.FloatMessage();
+                       case TC_STRING:
+                               return new DuskMessage.StringMessage();
+                       case TC_STRING_LIST:
+                               return new DuskMessage.StringListMessage();
+                       case TC_ENTITY_LIST:
+                               return new EntityListMessage();
+                       case TC_ENTITY_NOTIFY:
+                               return new EntityMessage();
+                       case TC_ENTITY_BYTE:
+                               return new EntityByteMessage();
+                       case TC_ENTITY_INTEGER:
+                               return new EntityIntegerMessage();
+                       case TC_ENTITY_LONG:
+                               return new EntityLongMessage();
+                       case TC_ENTITY_FLOAT:
+                               return new EntityFloatMessage();
+                       case TC_ENTITY_STRING:
+                               return new EntityStringMessage();
+                       case TC_ENTITY_STRING_LIST:
+                               return new EntityStringListMessage();
+                       case TC_MAP:
+                               return new MapMessage();
+                       case TC_ENTITY_UPDATE:
+                               return new EntityUpdateMessage();
+                       case TC_TRANSACTION:
+                               return new TransactionMessage();
+                       default:
+                               return null;
+               }
+       }
+
+       public void sendMessage(DataOutputStream ostream) throws IOException {
+               ostream.writeByte(getType());
+               send(ostream);
+       }
+
+       public static DuskMessage receiveMessage(DataInputStream istream) throws IOException {
+               int tc = istream.read();
+               DuskMessage a = createForType(tc);
+               if (a == null)
+                       throw new IOException("Unknown message tc: " + tc);
+
+               a.receive(istream);
+               return a;
+       }
+
+       static public DuskMessage create(int name) {
+               return new DuskMessage(name);
+       }
+
+       static public ByteMessage create(int name, byte value) {
+               return new ByteMessage(name, value);
+       }
+
+       static public IntegerMessage create(int name, int value) {
+               return new IntegerMessage(name, value);
+       }
+
+       static public LongMessage create(int name, long value) {
+               return new LongMessage(name, value);
+       }
+
+       static public FloatMessage create(int name, float value) {
+               return new FloatMessage(name, value);
+       }
+
+       static public StringMessage create(int name, String value) {
+               return new StringMessage(name, value);
+       }
+
+       static public StringListMessage create(int name, List<String> value) {
+               return new StringListMessage(name, value);
+       }
+
+       static public EntityMessage create(long id, int name) {
+               return new EntityMessage(id, name);
+       }
+
+       static public EntityByteMessage create(long id, int name, byte value) {
+               return new EntityByteMessage(id, name, value);
+       }
+
+       static public EntityIntegerMessage create(long id, int name, int value) {
+               return new EntityIntegerMessage(id, name, value);
+       }
+
+       static public EntityLongMessage create(long id, int name, long value) {
+               return new EntityLongMessage(id, name, value);
+       }
+
+       static public EntityFloatMessage create(long id, int name, float value) {
+               return new EntityFloatMessage(id, name, value);
+       }
+
+       static public EntityStringMessage create(long id, int name, String value) {
+               return new EntityStringMessage(id, name, value);
+       }
+
+       static public EntityStringListMessage create(long id, int name, List<String> value) {
+               return new EntityStringListMessage(id, name, value);
+       }
+
+       public static class ByteMessage extends DuskMessage {
+
+               public byte value;
+
+               public ByteMessage() {
+               }
+
+               public ByteMessage(int name) {
+                       super(name);
+               }
+
+               public ByteMessage(int name, byte value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeByte(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readByte();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_BYTE;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value=%d\n", s, getClass().getSimpleName(), name, value);
+               }
+       }
+
+       public static class IntegerMessage extends DuskMessage {
+
+               public int value;
+
+               public IntegerMessage() {
+               }
+
+               public IntegerMessage(int name) {
+                       super(name);
+               }
+
+               public IntegerMessage(int name, int value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeInt(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readInt();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_INTEGER;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value=%d\n", s, getClass().getSimpleName(), name, value);
+               }
+       }
+
+       public static class LongMessage extends DuskMessage {
+
+               public long value;
+
+               public LongMessage() {
+               }
+
+               public LongMessage(int name) {
+                       super(name);
+               }
+
+               public LongMessage(int name, long value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeLong(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readLong();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_LONG;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value=%d\n", s, getClass().getSimpleName(), name, value);
+               }
+       }
+
+       public static class FloatMessage extends DuskMessage {
+
+               public float value;
+
+               public FloatMessage() {
+               }
+
+               public FloatMessage(int name) {
+                       super(name);
+               }
+
+               public FloatMessage(int name, float value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeFloat(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readFloat();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_FLOAT;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value=%f\n", s, getClass().getSimpleName(), name, value);
+               }
+       }
+
+       public static class StringMessage extends DuskMessage {
+
+               public String value;
+
+               public StringMessage() {
+               }
+
+               public StringMessage(int name) {
+                       super(name);
+               }
+
+               public StringMessage(int name, String value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               @Override
+               public String toString() {
+                       return value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeUTF(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readUTF();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_STRING;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value=%s\n", s, getClass().getSimpleName(), name, value);
+               }
+       }
+
+       public static class StringListMessage extends DuskMessage {
+
+               public final List<String> value;
+
+               public StringListMessage() {
+                       value = new ArrayList<>();
+               }
+
+               public StringListMessage(int name) {
+                       super(name);
+                       value = new ArrayList<>();
+               }
+
+               public StringListMessage(int name, List<String> value) {
+                       super(name);
+                       this.value = value;
+               }
+
+               public void add(String v) {
+                       value.add(v);
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeShort(value.size());
+                       for (String s : value) {
+                               ostream.writeUTF(s);
+                       }
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       int len = istream.readShort() & 0xffff;
+
+                       for (int i = 0; i < len; i++)
+                               value.add(istream.readUTF());
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_STRING_LIST;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s name=%d value= {\n", s, getClass().getSimpleName(), name);
+                       for (String v : value) {
+                               out.printf("%s  '%s'\n", s, v);
+                       }
+                       out.printf("%s}\n", s);
+               }
+       }
+
+       public static class EntityMessage extends DuskMessage {
+
+               /**
+                * Id of entity this attribute applies to
+                */
+               public long id;
+
+               public EntityMessage() {
+               }
+
+               public EntityMessage(long id, int name) {
+                       this.id = id;
+                       this.name = name;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeLong(id);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       id = istream.readLong();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_NOTIFY;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d\n", s, getClass().getSimpleName(), id, name);
+               }
+       }
+
+       public static class EntityByteMessage extends EntityMessage {
+
+               public byte value;
+
+               public EntityByteMessage() {
+               }
+
+               public EntityByteMessage(long id, int name) {
+                       super(id, name);
+               }
+
+               public EntityByteMessage(long id, int name, byte value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeByte(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readByte();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_BYTE;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value=%d\n", s, getClass().getSimpleName(), id, name, value);
+               }
+       }
+
+       public static class EntityIntegerMessage extends EntityMessage {
+
+               public int value;
+
+               public EntityIntegerMessage() {
+               }
+
+               public EntityIntegerMessage(long id, int name) {
+                       super(id, name);
+               }
+
+               public EntityIntegerMessage(long id, int name, int value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeInt(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readInt();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_INTEGER;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value=%d\n", s, getClass().getSimpleName(), id, name, value);
+               }
+       }
+
+       public static class EntityLongMessage extends EntityMessage {
+
+               public long value;
+
+               public EntityLongMessage() {
+               }
+
+               public EntityLongMessage(long id, int name) {
+                       super(id, name);
+               }
+
+               public EntityLongMessage(long id, int name, long value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeLong(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readLong();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_LONG;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value=%d\n", s, getClass().getSimpleName(), id, name, value);
+               }
+       }
+
+       public static class EntityFloatMessage extends EntityMessage {
+
+               public float value;
+
+               public EntityFloatMessage() {
+               }
+
+               public EntityFloatMessage(long id, int name) {
+                       super(id, name);
+               }
+
+               public EntityFloatMessage(long id, int name, float value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeFloat(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readFloat();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_FLOAT;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value=%f\n", s, getClass().getSimpleName(), id, name, value);
+               }
+       }
+
+       public static class EntityStringMessage extends EntityMessage {
+
+               public String value;
+
+               public EntityStringMessage() {
+               }
+
+               public EntityStringMessage(long id, int name) {
+                       super(id, name);
+               }
+
+               public EntityStringMessage(long id, int name, String value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeUTF(value);
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       value = istream.readUTF();
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_STRING;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value=%s\n", s, getClass().getSimpleName(), id, name, value);
+               }
+       }
+
+       public static class EntityStringListMessage extends EntityMessage {
+
+               public final List<String> value;
+
+               public EntityStringListMessage() {
+                       value = new ArrayList<>();
+               }
+
+               public EntityStringListMessage(long id, int name) {
+                       super(id, name);
+                       value = new ArrayList<>();
+               }
+
+               public EntityStringListMessage(long id, int name, List<String> value) {
+                       super(id, name);
+                       this.value = value;
+               }
+
+               @Override
+               public void send(DataOutputStream ostream) throws IOException {
+                       super.send(ostream);
+                       ostream.writeShort(value.size());
+                       for (String s : value) {
+                               ostream.writeUTF(s);
+                       }
+               }
+
+               @Override
+               public void receive(DataInputStream istream) throws IOException {
+                       super.receive(istream);
+                       int len = istream.readShort() & 0xffff;
+
+                       for (int i = 0; i < len; i++)
+                               value.add(istream.readUTF());
+               }
+
+               @Override
+               public byte getType() {
+                       return TC_ENTITY_STRING_LIST;
+               }
+
+               @Override
+               protected void format(PrintStream out, String s) {
+                       out.printf("%s%s id=%d name=%d value= {\n", s, getClass().getSimpleName(), id, name);
+                       for (String v : value) {
+                               out.printf("%s  '%s'\n", s, v);
+                       }
+                       out.printf("%s}\n", s);
+               }
+       }
+}
diff --git a/DuskCommon/src/duskz/protocol/DuskProtocol.java b/DuskCommon/src/duskz/protocol/DuskProtocol.java
new file mode 100644 (file)
index 0000000..1dfb068
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+/**
+ * Although the message type could be used as a secondary index, instead
+ * i've decided to just use the message name alone. The first way
+ * would allow 256 messages per type, but 256 is enough total anyway.
+ *
+ * FIXME: the messages which just update various client state could be
+ * merged into a general "update" message which has variable content.
+ * FIXME: do this before freezing the protocol
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public interface DuskProtocol {
+       // Notify messages, hmm, maybe some of these should contain a string
+
+       /**
+        * MessageList
+        * I've tried to make this as simple as possible whilst retaining flexibility.
+        * Everything goes through a single message type, with optional fields.
+        *
+        * From Server:
+        * FIELD_AUTH_RESULT 0=ok,1=failed,2=exists
+        * FIELD_AUTH_NEWPLAYER (list of missing things, like race)
+        * To Server:
+        * FIELD_AUTH_USER
+        * FIELD_AUTH_PASS (hashed?)
+        * FIELD_AITH_CLIENT (applet/jar, etc)
+        * FIELD_AUTH_NEWPLAYER (list of missing things, like race)
+        */
+       public final static int MSG_AUTH = 0;
+       /**
+        * Notify only
+        * One ping and one ping only.
+        */
+       public final static int MSG_PING = 1;
+       /**
+        * Notify only
+        * Server dumped you
+        */
+       public final static int MSG_QUIT = 2;
+       /**
+        * TODO: I need some sort of question-asking mechanism.
+        *
+        * Probably work similar to the NEWPLAYER thing above.
+        * EntityListMessage with id set to unique value
+        * Each entry in the list is a ListMessage whose
+        * id is server defined.
+        * Each list message contains:
+        * StringMessage FIELD_QUERY_PROMPT
+        * optional
+        * StringListMessage FIELD_QUERY_OPTIONS
+        */
+       public final static int MSG_QUERY = 3;
+       /**
+        * Notify only
+        * Clear 'battle' flags.
+        */
+       public final static int MSG_CLEAR_FLAGS = 4;
+       /**
+        * ListMessage
+        * int FIELD_MAP_WIDTH
+        * int FIELD_MAP_HEIGHT
+        * TODO: merge this with other init stuff like images
+        */
+       public final static int MSG_INIT_MAP = 5;
+       /**
+        * MapMessage
+        * Update the map tiles
+        */
+       public final static int MSG_UPDATE_MAP = 6;
+       /**
+        * StringAttribute
+        * General chat
+        */
+       public final static int MSG_CHAT = 7;
+       /**
+        * EntityMessage
+        * Add a new entity
+        */
+       public final static int MSG_ADD_ENTITY = 8;
+       /**
+        * ListMessage
+        * see entity fields
+        */
+       public final static int MSG_UPDATE_ENTITY = 9;
+       /**
+        * EntityMessage
+        * id is of message to remove
+        */
+       public final static int MSG_REMOVE_ENTITY = 10;
+       /**
+        * ByteMessage
+        * Value is cardinal direction as byte (nsew)
+        */
+       public final static int MSG_MOVE = 11;
+       /**
+        * StringListAttribute
+        * list of actions
+        */
+       public final static int MSG_UPDATE_ACTIONS = 12;
+       /**
+        * TransactionMessage
+        * List of items the user has.
+        */
+       public final static int MSG_INVENTORY = 13;
+       /**
+        * ListMessage
+        * A list of AttributeString values.
+        * Name of fields is the index of the position worn
+        * (See Wearing)
+        */
+       public final static int MSG_EQUIPMENT = 14;
+       /**
+        * ListMessage
+        * Any of the fields in the INFO section below
+        */
+       public final static int MSG_INFO_PLAYER = 15;
+       /**
+        * ListMessage
+        * Any of the fields in the INFO section below
+        * except for FOLLOWING/FOLLOWED and the STATS (?)
+        */
+       public final static int MSG_INFO_PET = 16;
+       /**
+        * TransactionMessage
+        * List of items for sale
+        */
+       public final static int MSG_UPDATE_MERCHANT = 17;
+       /**
+        * Notify only
+        * The player left a shop
+        */
+       public final static int MSG_EXIT_MERCHANT = 18;
+       /**
+        * Start of battle.
+        * long FIELD_BATTLE_SOURCE (TODO)
+        * string FIELD_BATTLE_OPPONENT
+        */
+       public final static int MSG_BATTLE_START = 19;
+       /**
+        * ListMessage
+        * long FIELD_BATTLE_TARGET who got it
+        * int FIELD_BATTLE_HP target hp
+        * int FIELD_BATTLE_MAXHP target max hp
+        * int FIELD_BATTLE_DAMAGE +- damage
+        * long FIELD_BATTLE_SOURCE who did it
+        * string FIELD_BATTLE_WHAT how it was done
+        */
+       public final static int MSG_BATTLE_UPDATE = 20;
+       /**
+        * StringAttribute
+        * Battle related messages
+        */
+       public final static int MSG_BATTLE_CHAT = 21;
+       /**
+        * ListMessage
+        * string FIELD_TEXT_NAME
+        * byte FIELD_TEXT_EDITABLE
+        * string FIELD_TEXT_TEXT
+        */
+       public final static int MSG_VIEW_TEXT = 22;
+       /**
+        * *********************************************
+        * Client to server messages.
+        *
+        * MSG_AUTH and MSG_QUERY are also included
+        */
+       /**
+        * StringMessage
+        * A simple string message containing the command to run
+        */
+       public final static int MSG_COMMAND = 23;
+       //
+       //
+       //
+       //TODO:
+       //LoadMusic,
+       //PlayMusic,
+       //PlaySound,
+       //ColourChat,
+       /**
+        * MSG_AUTH fields
+        */
+       public final static int AUTH_LOGIN_OK = 0;
+       public final static int AUTH_LOGIN_FAILED = 1;
+       public final static int AUTH_LOGIN_EXISTS = 2;
+       public final static int AUTH_LOGIN_INCOMPLETE = 3;
+       /**
+        * EntityIntegerMessage
+        * Result of authentication,as above
+        * if ok, id = id of player
+        */
+       public final static int FIELD_AUTH_RESULT = 0;
+       /**
+        * StringMessage
+        * reason/prompt for failure
+        */
+       public final static int FIELD_AUTH_REASON = 1;
+       /**
+        * ListMessage of ListMessage
+        * server: create failed because of missing information, list lists it.
+        * The name of each message is for the server, and each list describes
+        * a query using the FIELD_QUERY constants
+        * client: new player extra information. i.e. race, email, whatever.
+        * The name of each response should match those in the server list.
+        *
+        */
+       public final static int FIELD_AUTH_NEWPLAYER = 2;
+       /**
+        * StringMessage
+        * client: player name
+        */
+       public final static int FIELD_AUTH_PLAYER = 3;
+       /**
+        * StringMessage
+        * client: player password
+        */
+       public final static int FIELD_AUTH_PASS = 4;
+       /**
+        * ListMessage
+        * client: Details about client
+        */
+       public final static int FIELD_AUTH_CLIENT = 5;
+       /**
+        * Query fields
+        * These are fields within each ListMessage in
+        * NEWPLAYER or QUERY message
+        * TODO: could add more types
+        */
+       /**
+        * StringMessage
+        * server: prompt for query
+        */
+       public final static int FIELD_QUERY_PROMPT = 0;
+       /**
+        * StringListMessage
+        * server: a list of options to choose from. The client
+        * response will be a String with a name which matches the
+        * ListMessage containing this item.
+        */
+       public final static int FIELD_QUERY_OPTIONS = 1;
+       /**
+        * StringMessage
+        * Present a password box
+        * server: string is prompt
+        * client: string is response
+        */
+       public final static int FIELD_QUERY_HIDDEN = 2;
+       // TOOD: could add more options here.
+       /**
+        * Entity update fields
+        */
+       public final static int FIELD_ENTITY_FLAGS = 0;
+       /**
+        * Player update fields
+        * MSG_INFO_PLAYER
+        * MSG_INFO_PET
+        */
+       /**
+        * IntegerMessage
+        * Player hp
+        */
+       public final static int FIELD_INFO_HP = 0;
+       /**
+        * IntegerMessage
+        * max hp
+        */
+       public final static int FIELD_INFO_MAXHP = 1;
+       /**
+        * IntegerMessage
+        * player mp
+        */
+       public final static int FIELD_INFO_MP = 2;
+       /**
+        * IntegerMessage
+        * max mp
+        */
+       public final static int FIELD_INFO_MAXMP = 3;
+       /**
+        * LongMessage
+        * Money in hand
+        */
+       public final static int FIELD_INFO_CASH = 4;
+       /**
+        * IntegerMessage
+        * Experience
+        */
+       public final static int FIELD_INFO_EXP = 5;
+       /**
+        * IntegerMessage
+        * Strength
+        */
+       public final static int FIELD_INFO_STR = 6;
+       /**
+        * IntegerMessage
+        * Strength bonus - if not zero
+        */
+       public final static int FIELD_INFO_STRBON = 7;
+       /**
+        * IntegerMessage
+        * Intelligence
+        */
+       public final static int FIELD_INFO_INT = 8;
+       /**
+        * IntegerMessage
+        * Intelligence bonus if not zero
+        */
+       public final static int FIELD_INFO_INTBON = 9;
+       /**
+        * IntegerMessage
+        * Dexterity
+        */
+       public final static int FIELD_INFO_DEX = 10;
+       /**
+        * IntegerMessage
+        * Dexterity bonus if not zero
+        */
+       public final static int FIELD_INFO_DEXBON = 11;
+       /**
+        * IntegerMessage
+        * You get the picture
+        */
+       public final static int FIELD_INFO_CON = 12;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_CONBON = 13;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_WIS = 14;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_WISBON = 15;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_DAM = 16;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_DAMBON = 17;
+       /**
+        * IntegerMessage
+        * Armour class
+        */
+       public final static int FIELD_INFO_ARC = 18;
+       /**
+        * IntegerMessage
+        *
+        */
+       public final static int FIELD_INFO_ARCBON = 19;
+       /**
+        * StringArrayMessage
+        * A list of conditions active on the player/pet
+        */
+       public final static int FIELD_INFO_CONDITIONS = 20;
+       /**
+        * StringArrayMessage
+        * List of skills the player has
+        */
+       public final static int FIELD_INFO_SKILLS = 21;
+       /**
+        * StringArrayMessage
+        * List of spells the player has
+        * FIXME: do this in a way that i know the castable spells in the frontend,
+        * even if it's just string parsing
+        */
+       public final static int FIELD_INFO_SPELLS = 22;
+       /**
+        * StringMessage
+        * Who player is following
+        */
+       public final static int FIELD_INFO_FOLLOWING = 23;
+       /**
+        * StringMessage
+        * Who player is being followed by (pet)
+        */
+       public final static int FIELD_INFO_FOLLOWED = 24;
+       /**
+        * IntegerMessage
+        * Player attack range
+        */
+       public final static int FIELD_INFO_RANGE = 25;
+       /**
+        * MSG_INIT_MAP fields
+        */
+       /**
+        * IntegerMessage
+        * width of displayed map
+        */
+       public final static int FIELD_MAP_WIDTH = 0;
+       /**
+        * IntegerMessage
+        * height of displayed map
+        */
+       public final static int FIELD_MAP_HEIGHT = 1;
+       /**
+        * StringMessage
+        * location of asset jar for this map
+        */
+       public final static int FIELD_MAP_ASSETLOCATION = 2;
+       /**
+        * MSG_VIEW_TEXT
+        */
+       /**
+        * StringMessage
+        * Name of file/title of content
+        */
+       public final static int FIELD_TEXT_NAME = 0;
+       /**
+        * ByteMessage
+        * Present only if true (==1), message is editable
+        */
+       public final static int FIELD_TEXT_EDITABLE = 1;
+       /**
+        * StringMessage
+        * Content of file
+        */
+       public final static int FIELD_TEXT_TEXT = 2;
+       /**
+        * MSG_BATTLE_UPDATE
+        */
+       /**
+        * LongMessage
+        * id of target of message/hit
+        */
+       public final static int FIELD_BATTLE_TARGET = 0;
+       /**
+        * IntegerMessage
+        * current hp
+        */
+       public final static int FIELD_BATTLE_HP = 1;
+       /**
+        * IntegerMessage
+        * maximum hp
+        */
+       public final static int FIELD_BATTLE_MAXHP = 2;
+       /**
+        * IntegerMessage
+        * +/- healing/damage from some action
+        */
+       public final static int FIELD_BATTLE_DAMAGE = 3;
+       /**
+        * LongMessage
+        * Who caused the damage/healing
+        */
+       public final static int FIELD_BATTLE_SOURCE = 4;
+       /**
+        * What it was they did
+        */
+       public final static int FIELD_BATTLE_WHAT = 5;
+       /**
+        * MSG_BATTLE_START
+        * FIXME: Will also include FIELD_BATTLE_SOURCE for
+        * the opponent id
+        */
+       /**
+        * StringMessage
+        * Name of opponent
+        */
+       public final static int FIELD_BATTLE_OPPONENT = 6;
+}
diff --git a/DuskCommon/src/duskz/protocol/EntityListMessage.java b/DuskCommon/src/duskz/protocol/EntityListMessage.java
new file mode 100644 (file)
index 0000000..dd7f75f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * A list message with a target.
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class EntityListMessage extends ListMessage {
+
+       public long id;
+
+       EntityListMessage() {
+       }
+
+       public EntityListMessage(long id, int name) {
+               super(name);
+               this.id = id;
+       }
+
+       @Override
+       public byte getType() {
+               return TC_ENTITY_LIST;
+       }
+
+       @Override
+       public void send(DataOutputStream ostream) throws IOException {
+               super.send(ostream);
+               ostream.writeLong(id);
+       }
+
+       @Override
+       public void receive(DataInputStream istream) throws IOException {
+               super.receive(istream);
+               id = istream.readLong();
+       }
+}
diff --git a/DuskCommon/src/duskz/protocol/EntityUpdateMessage.java b/DuskCommon/src/duskz/protocol/EntityUpdateMessage.java
new file mode 100644 (file)
index 0000000..d754b03
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * An entity update.
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class EntityUpdateMessage extends DuskMessage.EntityMessage {
+
+       public String entityName;
+       public byte entityType;
+       public short x;
+       public short y;
+       public short image;
+       public short imageStep;
+
+       @Override
+       public void send(DataOutputStream ostream) throws IOException {
+               super.send(ostream);
+               ostream.writeLong(id);
+               ostream.writeUTF(entityName);
+               ostream.writeByte(entityType);
+               ostream.writeShort(x);
+               ostream.writeShort(y);
+               ostream.writeShort(image);
+               ostream.writeShort(imageStep);
+       }
+
+       @Override
+       public void receive(DataInputStream istream) throws IOException {
+               super.receive(istream);
+               id = istream.readLong();
+               entityName = istream.readUTF();
+               entityType = istream.readByte();
+               x = istream.readShort();
+               y = istream.readShort();
+               image = istream.readShort();
+               imageStep = istream.readShort();
+       }
+
+       @Override
+       public byte getType() {
+               return TC_ENTITY_UPDATE;
+       }
+}
diff --git a/DuskCommon/src/duskz/protocol/ListMessage.java b/DuskCommon/src/duskz/protocol/ListMessage.java
new file mode 100644 (file)
index 0000000..38fec03
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DuskMessage for a list of named value
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class ListMessage extends DuskMessage {
+
+       final public List<DuskMessage> value;
+
+       ListMessage() {
+               value = new ArrayList<>();
+       }
+
+       public ListMessage(int name) {
+               super(name);
+               value = new ArrayList<>();
+       }
+
+       public void add(DuskMessage a) {
+               value.add(a);
+       }
+
+       public void add(int name, byte value) {
+               this.value.add(DuskMessage.create(name, value));
+       }
+
+       public void add(int name, int value) {
+               this.value.add(DuskMessage.create(name, value));
+       }
+
+       public void add(int name, long value) {
+               this.value.add(DuskMessage.create(name, value));
+       }
+
+       public void add(int name, String value) {
+               this.value.add(DuskMessage.create(name, value));
+       }
+
+       public void add(long id, int name, int value) {
+               this.value.add(DuskMessage.create(id, name, value));
+       }
+       public void add(long id, int name, String value) {
+               this.value.add(DuskMessage.create(id, name, value));
+       }
+
+       public DuskMessage.StringListMessage add(int name, List<String> value) {
+               StringListMessage a = DuskMessage.create(name, value);
+               this.value.add(a);
+               return a;
+       }
+
+       public DuskMessage get(int name) {
+               int len = value.size();
+               for (int i = 0; i < len; i++) {
+                       DuskMessage a = value.get(i);
+                       if (a.name == name)
+                               return a;
+               }
+               return null;
+       }
+
+       public byte getByte(int name) {
+               return ((ByteMessage) get(name)).value;
+       }
+
+       public int getInteger(int name) {
+               DuskMessage m = get(name);
+               
+               if (m instanceof IntegerMessage)
+                       return ((IntegerMessage)m).value;
+               else if (m instanceof EntityIntegerMessage) {
+                       return ((EntityIntegerMessage)m).value;
+               }
+               throw new ClassCastException(m.getClass().getName() + " not IntegerMessage");
+       }
+
+       public long getLong(int name) {
+               return ((LongMessage) get(name)).value;
+       }
+
+       public String getString(int name) {
+               return ((StringMessage) get(name)).value;
+       }
+
+       public List<String> getStringList(int name) {
+               return ((StringListMessage) get(name)).value;
+       }
+
+       public byte getByte(int name, byte def) {
+               DuskMessage a = get(name);
+
+               if (a == null || a.getType() != TC_BYTE)
+                       return def;
+
+               return ((ByteMessage) a).value;
+       }
+
+       public int getInteger(int name, int def) {
+               DuskMessage a = get(name);
+
+               if (a == null || a.getType() != TC_INTEGER)
+                       return def;
+               return ((IntegerMessage) a).value;
+       }
+
+       public long getLong(int name, long def) {
+               DuskMessage a = get(name);
+
+               if (a == null || a.getType() != TC_INTEGER)
+                       return def;
+               return ((LongMessage) a).value;
+       }
+
+       public String getString(int name, String def) {
+               DuskMessage a = get(name);
+
+               if (a == null || a.getType() != TC_STRING)
+                       return def;
+               return ((StringMessage) a).value;
+       }
+
+       public List<String> getStringList(int name, List<String> def) {
+               DuskMessage a = get(name);
+
+               if (a == null || a.getType() != TC_STRING_LIST)
+                       return def;
+               return ((StringListMessage) a).value;
+       }
+
+       public DuskMessage getMessage(int name) {
+               return get(name);
+       }
+
+       @Override
+       public void send(DataOutputStream ostream) throws IOException {
+               super.send(ostream);
+               ostream.writeShort(value.size());
+               for (DuskMessage a : value) {
+                       ostream.writeByte(a.getType());
+                       a.send(ostream);
+               }
+       }
+
+       @Override
+       public void receive(DataInputStream istream) throws IOException {
+               super.receive(istream);
+
+               int len = istream.readShort();
+               for (int i = 0; i < len; i++) {
+                       value.add(receiveMessage(istream));
+               }
+       }
+
+       @Override
+       public byte getType() {
+               return TC_LIST;
+       }
+
+       @Override
+       protected void format(PrintStream out, String s) {
+               out.printf("%s%s name=%d value = {\n", s, getClass().getSimpleName(), name);
+               for (DuskMessage dm : value) {
+                       dm.format(out, s + "\t");
+               }
+               out.printf("%s}\n", s);
+       }
+}
diff --git a/DuskCommon/src/duskz/protocol/MapMessage.java b/DuskCommon/src/duskz/protocol/MapMessage.java
new file mode 100644 (file)
index 0000000..5917aaa
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * DuskMessage for a client view map update.
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class MapMessage extends DuskMessage {
+
+       public short x;
+       public short y;
+       public short width;
+       public short height;
+       public short[] map;
+
+       public MapMessage() {
+       }
+
+       public MapMessage(int name, int width, int height, int locx, int locy, short[] map) {
+               super(name);
+
+               this.x = (short) locx;
+               this.y = (short) locy;
+               this.width = (short) width;
+               this.height = (short) height;
+               this.map = map;
+       }
+
+       @Override
+       public void send(DataOutputStream ostream) throws IOException {
+               super.send(ostream);
+               ostream.writeShort(x);
+               ostream.writeShort(y);
+               ostream.writeShort(width);
+               ostream.writeShort(height);
+               for (int i = 0; i < width * height; i++)
+                       ostream.writeShort(map[i]);
+       }
+
+       @Override
+       public void receive(DataInputStream istream) throws IOException {
+               int len;
+
+               super.receive(istream);
+               x = istream.readShort();
+               y = istream.readShort();
+               width = istream.readShort();
+               height = istream.readShort();
+               len = width * height;
+               map = new short[len];
+               for (int i = 0; i < len; i++) {
+                       map[i] = istream.readShort();
+               }
+       }
+
+       @Override
+       public byte getType() {
+               return TC_MAP;
+       }
+}
diff --git a/DuskCommon/src/duskz/protocol/MessageType.java b/DuskCommon/src/duskz/protocol/MessageType.java
deleted file mode 100644 (file)
index fee4a92..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * This file is part of DuskZ, a graphical mud engine.
- *
- * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
- *
- * DuskZ is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * DuskZ 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with DuskZ; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-/**
- * Changes
- */
-package duskz.protocol;
-
-import java.io.DataInputStream;
-import java.io.IOException;
-
-/**
- * Type of message sent from server.
- *
- * @author notzed
- */
-public enum MessageType {
-
-       // 0
-       Quit,
-       UpdateImages,
-       UpdateLocMap {
-
-               @Override
-               public ServerMessage decode(DataInputStream instream) throws IOException {
-                       return ServerMessage.MapMessage.decode(instream);
-               }               
-       },
-       Chat,
-       AddEntity,
-       // 5
-       UpdateStats,
-       UpdateItems,
-       UpdateEquipment,
-       UpdateInfo,
-       Halt,
-       // 10
-       UpdateActions,
-       LoadMusic,
-       PlayMusic,
-       Ping,
-       Proceed,
-       // 15
-       PlaySound,
-       RemoveEntity,
-       UpdateMerchant,
-       EditText,
-       ResizeMap,
-       // 20
-       ViewText,
-       ExitMerchant,
-       UpdateSell,
-       ColourChat,
-       MoveNorth,
-       // 25
-       MoveSouth,
-       MoveWest,
-       MoveEast,
-       UpdateRange,
-       SetFlag,
-       // 30
-       ClearFlags,
-       StartBattle,
-       UpdateBattle,
-       LogBattle,
-       // Above list is compatible with original dusk, new ones follow
-       // A bunch of login/setup related stuff
-       /**
-        * Choose race, response is
-        * code header
-        * race 0
-        * race 1
-        * ...
-        * .
-        */
-       ChooseRace,
-       /**
-        * Damage (or healing) to an entity.
-        * Encoded as:
-        * targetID
-        * +-damage
-        * newhp
-        * totalhp
-        * fromID
-        * how
-        * .
-        */
-       HitEntity,;
-
-       public static MessageType fromServer(int v) {
-               return values()[v];
-       }
-
-       public char code() {
-               return (char) ordinal();
-       }
-
-       /**
-        * Not implemented yet
-        * @param instream
-        * @return
-        * @throws IOException
-        * @deprecated
-        */
-       @Deprecated
-       public ServerMessage decode(DataInputStream instream) throws IOException {
-               return null;
-       }
-}
diff --git a/DuskCommon/src/duskz/protocol/ServerMessage.java b/DuskCommon/src/duskz/protocol/ServerMessage.java
deleted file mode 100644 (file)
index a133fd2..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * This file is part of DuskZ, a graphical mud engine.
- *
- * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
- *
- * DuskZ is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * DuskZ 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with DuskZ; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-/**
- * Changes
- */
-package duskz.protocol;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Message from server to client
- *
- * @author notzed
- */
-public abstract class ServerMessage {
-
-       final MessageType type;
-
-       public ServerMessage(MessageType type) {
-               this.type = type;
-       }
-
-       public abstract void send(DataOutputStream ostream) throws IOException;
-
-       public static ServerMessage stringMessage(String s) {
-               return new StringMessage(s);
-       }
-
-       public static ServerMessage stringMessage(MessageType type, String data) {
-               return new StringMessage(type, data);
-       }
-
-       public static ServerMessage mapMessage(int locx, int locy, short[] map) {
-               return new MapMessage(locx, locy, map);
-       }
-
-       public static ServerMessage fromServer(DataInputStream instream) throws IOException {
-               int typec = instream.readByte();
-
-               MessageType type = MessageType.values()[typec];
-               // TODO: more protocol, length?  how?
-
-               return type.decode(instream);
-       }
-
-       public static class StringMessage extends ServerMessage {
-
-               final String data;
-
-               public StringMessage(String data) {
-                       super(null);
-                       this.data = data;
-               }
-
-               private StringMessage(MessageType type, String data) {
-                       super(type);
-                       this.data = data;
-               }
-
-               @Override
-               public void send(DataOutputStream ostream) throws IOException {
-                       if (type != null)
-                               ostream.writeByte(type.code());
-                       ostream.writeBytes(data);
-               }
-
-               @Override
-               public String toString() {
-                       return data;
-               }
-       }
-
-       public static class MapMessage extends ServerMessage {
-
-               public int x;
-               public int y;
-               public short[] map;
-
-               public MapMessage(int locx, int locy, short[] map) {
-                       super(MessageType.UpdateLocMap);
-                       this.x = locx;
-                       this.y = locy;
-                       this.map = map;
-               }
-
-               public static MapMessage decode(DataInputStream instream) throws IOException {
-                       int x = instream.readShort();
-                       int y = instream.readShort();
-                       int length = instream.readInt();
-                       short[] map = new short[length];
-                       for (int i = 0; i < length; i++) {
-                               map[i] = instream.readShort();
-                       }
-                       return new MapMessage(x, y, map);
-               }
-
-               @Override
-               public void send(DataOutputStream ostream) throws IOException {
-                       ostream.writeByte(type.code());
-                       ostream.writeShort(x);
-                       ostream.writeShort(y);
-                       ostream.writeInt(map.length);
-                       for (int i = 0; i < map.length; i++)
-                               ostream.writeShort(map[i]);
-               }
-       }
-
-       public static class EntityMessage extends ServerMessage {
-
-               public long id;
-               public String name;
-               public byte entityType;
-               public short x;
-               public short y;
-               public short image;
-               public short imageStep;
-
-               public EntityMessage(MessageType type, long id, String name, byte entityType, short x, short y, short image, short imageStep) {
-                       super(type);
-                       this.id = id;
-                       this.name = name;
-                       this.entityType = entityType;
-                       this.x = x;
-                       this.y = y;
-                       this.image = image;
-                       this.imageStep = imageStep;
-
-                       System.out.printf("create entity message: %d '%s' %d %dx%d image %d %d\n",
-                                       id, name, entityType, x, y, image, imageStep);
-               }
-
-               public static EntityMessage decode(MessageType type, DataInputStream instream) throws IOException {
-                       long id = instream.readLong();
-                       String name = instream.readUTF();
-                       byte entityType = instream.readByte();
-                       short x = instream.readShort();
-                       short y = instream.readShort();
-                       short image = instream.readShort();
-                       short imageStep = instream.readShort();
-
-                       return new EntityMessage(type, id, name, entityType, x, y, image, imageStep);
-               }
-
-               @Override
-               public void send(DataOutputStream ostream) throws IOException {
-                       ostream.writeByte(type.code());
-                       ostream.writeLong(id);
-                       ostream.writeUTF(name);
-                       ostream.writeByte(entityType);
-                       ostream.writeShort(x);
-                       ostream.writeShort(y);
-                       ostream.writeShort(image);
-                       ostream.writeShort(imageStep);
-               }
-       }
-
-       /**
-        * Main player stats
-        */
-       public static class StatsMessage extends ServerMessage {
-
-               public long cash;
-               public int exp;
-               public int stre, strebonus;
-               public int inte, intebonus;
-               public int dext, dextbonus;
-               public int cons, consbonus;
-               public int wisd, wisdbonus;
-               public int damm, dammbonus;
-               public int ac, acbonus;
-
-               public StatsMessage(MessageType type, long cash, int exp, int stre, int strebonus,
-                               int inte, int intebonus, int dext, int dextbonus, int cons, int consbonus,
-                               int wisd, int wisdbonus, int damm, int dammbonus, int ac, int acbonus) {
-                       super(type);
-                       this.cash = cash;
-                       this.exp = exp;
-                       this.stre = stre;
-                       this.strebonus = strebonus;
-                       this.inte = inte;
-                       this.intebonus = intebonus;
-                       this.dext = dext;
-                       this.dextbonus = dextbonus;
-                       this.cons = cons;
-                       this.consbonus = consbonus;
-                       this.wisd = wisd;
-                       this.wisdbonus = wisdbonus;
-                       this.damm = damm;
-                       this.dammbonus = dammbonus;
-                       this.ac = ac;
-                       this.acbonus = acbonus;
-               }
-
-               public static StatsMessage decode(MessageType type, DataInputStream instream) throws IOException {
-                       long cash = instream.readLong();
-                       int exp = instream.readInt();
-                       int stre = instream.readInt();
-                       int strebonus = instream.readInt();
-                       int inte = instream.readInt();
-                       int intebonus = instream.readInt();
-                       int dext = instream.readInt();
-                       int dextbonus = instream.readInt();
-                       int cons = instream.readInt();
-                       int consbonus = instream.readInt();
-                       int wisd = instream.readInt();
-                       int wisdbonus = instream.readInt();
-                       int damm = instream.readInt();
-                       int dammbonus = instream.readInt();
-                       int ac = instream.readInt();
-                       int acbonus = instream.readInt();
-
-                       return new StatsMessage(type, cash, exp,
-                                       stre, strebonus,
-                                       inte, intebonus,
-                                       dext, dextbonus,
-                                       cons, consbonus,
-                                       wisd, wisdbonus,
-                                       damm, dammbonus,
-                                       ac, acbonus);
-               }
-
-               @Override
-               public void send(DataOutputStream ostream) throws IOException {
-                       ostream.writeByte(type.code());
-                       ostream.writeLong(cash);
-                       ostream.writeInt(exp);
-                       ostream.writeInt(stre);
-                       ostream.writeInt(strebonus);
-                       ostream.writeInt(inte);
-                       ostream.writeInt(intebonus);
-                       ostream.writeInt(dext);
-                       ostream.writeInt(dextbonus);
-                       ostream.writeInt(cons);
-                       ostream.writeInt(consbonus);
-                       ostream.writeInt(wisd);
-                       ostream.writeInt(wisdbonus);
-                       ostream.writeInt(damm);
-                       ostream.writeInt(dammbonus);
-                       ostream.writeInt(ac);
-                       ostream.writeInt(acbonus);
-               }
-       }
-
-       /**
-        * Other player stats?
-        * All together or separate?
-        * UpdateSkills
-        * UpdateConditions?
-        */
-       /*
-        strResult += "-Affected by-\n";
-        for (Condition cond : conditions) {
-        if (cond.display) {
-        strResult += cond.name + "\n";
-        }
-        }
-        strResult += "-Skills-\n";
-        for (Ability skill : skillMap.values()) {
-        strResult += skill.name + ": " + skill.getAbility() + "\n";
-        }
-        strResult += "-Spells-\n";
-        for (Ability spell : spellMap.values()) {
-        grpStore = game.getSpellGroup(spell.name);
-        if (grpStore != null) {
-        strResult += spell.name + ": " + spell.getAbility() + "\n";
-        strResult += grpStore.spellList(spell.getAbility());
-        }
-        }
-        if (master != null) {
-        strResult += "\nFollowing: " + master.name + "\n";
-        }*/
-       public static class ItemsMessage extends ServerMessage {
-
-               List<TransactionItem> items;
-
-               public ItemsMessage(MessageType type, List<TransactionItem> items) {
-                       super(type);
-                       this.items = items;
-               }
-
-               public static ItemsMessage decode(MessageType type, DataInputStream instream) throws IOException {
-                       List<TransactionItem> items = new ArrayList<>();
-
-                       int count = instream.readShort();
-                       for (int i = 0; i < count; i++) {
-                               TransactionItem item = new TransactionItem();
-
-                               item.name = instream.readUTF();
-                               item.count = instream.readInt();
-                               item.cost = instream.readInt();
-                               item.units = instream.readUTF();
-                       }
-
-                       return new ItemsMessage(type, items);
-               }
-
-               @Override
-               public void send(DataOutputStream ostream) throws IOException {
-                       ostream.writeByte(type.code());
-                       ostream.writeShort(items.size());
-                       for (TransactionItem item : items) {
-                               ostream.writeUTF(item.getName());
-                               ostream.writeInt(item.getCount());
-                               ostream.writeInt(item.getCost());
-                               ostream.writeUTF(item.getUnits());
-                       }
-               }
-       }
-}
index cead913..3a42cd8 100644 (file)
  */
 package duskz.protocol;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 /**
  * Represents a transaction item (buy/sell) for server protocol.
+ *
  * @author notzed
  */
-public class TransactionItem implements Comparable<TransactionItem>{
-
+public class TransactionItem implements Comparable<TransactionItem> {
+       
+       // From wearing
+       public int type;
        public String name;
        public int count;
        public int cost;
@@ -36,14 +43,14 @@ public class TransactionItem implements Comparable<TransactionItem>{
        public TransactionItem() {
        }
 
-       
-       public TransactionItem(String name, int count, int cost, String units) {
+       public TransactionItem(int type, String name, int count, int cost, String units) {
+               this.type = type;
                this.name = name;
                this.count = count;
                this.cost = cost;
                this.units = units;
        }
-       
+
        public String getName() {
                return name;
        }
@@ -56,17 +63,35 @@ public class TransactionItem implements Comparable<TransactionItem>{
                return cost;
        }
 
+       public String getCostText() {
+               return cost + " " + units;
+       }
+       
        public String getUnits() {
                return units;
        }
-               
+
        @Override
        public String toString() {
                return cost + ") " + name + "[" + count + "]";
        }
-       
+
        @Override
        public int compareTo(TransactionItem t) {
                return name.compareTo(t.name);
        }
+
+       public void send(DataOutputStream ostream) throws IOException {
+               ostream.writeUTF(name);
+               ostream.writeInt(count);
+               ostream.writeInt(cost);
+               ostream.writeUTF(units);
+       }
+
+       public void receive(DataInputStream istream) throws IOException {
+               name = istream.readUTF();
+               count = istream.readInt();
+               cost = istream.readInt();
+               units = istream.readUTF();
+       }
 }
diff --git a/DuskCommon/src/duskz/protocol/TransactionMessage.java b/DuskCommon/src/duskz/protocol/TransactionMessage.java
new file mode 100644 (file)
index 0000000..9484d4f
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.protocol;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A list of for buy or sell items.
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class TransactionMessage extends DuskMessage {
+
+       public final List<TransactionItem> items;
+
+       TransactionMessage() {
+               items = new ArrayList<>();
+       }
+
+       public TransactionMessage(int name) {
+               super(name);
+               items = new ArrayList<>();
+       }
+
+       public TransactionMessage(int name, List<TransactionItem> items) {
+               super(name);
+               this.items = items;
+       }
+
+       public void add(TransactionItem item) {
+               items.add(item);
+       }
+
+       public void add(int type, String name, int count, int cost, String units) {
+               items.add(new TransactionItem(type, name, count, cost, units));
+       }
+
+       @Override
+       public void send(DataOutputStream ostream) throws IOException {
+               super.send(ostream);
+               ostream.writeShort(items.size());
+               for (TransactionItem item : items) {
+                       item.send(ostream);
+               }
+       }
+
+       @Override
+       public void receive(DataInputStream istream) throws IOException {
+               super.receive(istream);
+               int count = istream.readShort();
+               for (int i = 0; i < count; i++) {
+                       TransactionItem item = new TransactionItem();
+                       item.receive(istream);
+                       items.add(item);
+               }
+       }
+
+       @Override
+       public byte getType() {
+               return TC_TRANSACTION;
+       }
+
+       @Override
+       protected void format(PrintStream out, String s) {
+               out.printf("%s%s name=%d value= {\n", s, getClass().getSimpleName(), name);
+               for (TransactionItem v : items) {
+                       out.printf("%s  %d '%s' %d %s\n", s, v.count, v.name, v.cost, v.units);
+               }
+               out.printf("%s}\n", s);
+       }
+}
index ccd254e..a86bb2b 100644 (file)
@@ -24,18 +24,50 @@ package duskz.protocol;
 
 /**
  * Constants for wearing locations.
+ *
  * @author notzed
  */
 public interface Wearing {
 
+       /**
+        * Non-wearable item
+        */
+       public static final int INVENTORY = -1;
+       /**
+        * Wieldable
+        */
        public static final int WIELD = 0;
+       /**
+        * Armour on arms
+        */
        public static final int ARMS = 1;
+       /**
+        * Armour on legs
+        */
        public static final int LEGS = 2;
+       /**
+        * Armour on body
+        */
        public static final int TORSO = 3;
+       /**
+        * Armour belt
+        */
        public static final int WAIST = 4;
+       /**
+        * Armour on neck
+        */
        public static final int NECK = 5;
+       /**
+        * Armour on head
+        */
        public static final int SKULL = 6;
+       /**
+        * Saftey goggles
+        */
        public static final int EYES = 7;
+       /**
+        * Gloves
+        */
        public static final int HANDS = 8;
        public static final int WEARING_COUNT = 9;
        public static final String[] titles = {
diff --git a/DuskServer/src/duskz/server/BannedIPException.java b/DuskServer/src/duskz/server/BannedIPException.java
new file mode 100644 (file)
index 0000000..af20cd2
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.server;
+
+/**
+ * IP was banned automatically
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class BannedIPException extends Exception {
+}
index 87418ed..38cd620 100644 (file)
 /**
  * Changes
  * Feb-2013 Michael Zucchi - Pretty major cleanup and parameterisation of code.
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server;
 
-import duskz.protocol.MessageType;
+import duskz.protocol.DuskProtocol;
+import duskz.protocol.ListMessage;
 import duskz.server.entity.Mob;
 import duskz.server.entity.LivingThing;
 import duskz.server.entity.Item;
@@ -39,7 +41,7 @@ import java.util.LinkedList;
  *
  * @author Tom Weingarten
  */
-public class Battle {
+public class Battle implements DuskProtocol {
 
        private ArrayList<LivingThing> vctSide1 = new ArrayList<>(),
                        vctSide2 = new ArrayList<>();
@@ -81,12 +83,8 @@ public class Battle {
                        }
                        thnFront1 = inpla1;
                        engGame.chatMessage("-" + inpla1.name + " has attacked " + inpla2.name, inpla1.x, inpla1.y, "default");
-                       if (inpla1.popup) {
-                               inpla1.send(MessageType.StartBattle, inpla2.name + "\n");
-                       }
-                       if (inpla2.popup) {
-                               inpla2.send(MessageType.StartBattle, inpla1.name + "\n");
-                       }
+                       inpla1.startBattle(inpla2);
+                       inpla2.startBattle(inpla1);
                } catch (Exception e) {
                        blnRunning = false;
                        engGame.log.printError("Battle()", e);
@@ -131,7 +129,7 @@ public class Battle {
                                thnAdded.playMusic(1);
                        }
                }
-               chatMessage("\t" + thnAdded.name + " has joined the battle.");
+               chatMessage(thnAdded.name + " has joined the battle.");
                side.add(thnAdded);
                thnAdded.enterBattle(this, sideid);
 
@@ -266,7 +264,7 @@ public class Battle {
                                }
                                msg += ".";
                        }
-                       chatMessage("\t" + msg);
+                       chatMessage(msg);
                }
                return true;
        }
@@ -303,7 +301,7 @@ public class Battle {
 
        void endBattle() {
                blnRunning = false;
-               chatMessage("\tYou have won the battle.");
+               chatMessage("You have won the battle.");
                endBattle(vctSide1);
                endBattle(vctSide2);
        }
@@ -330,11 +328,7 @@ public class Battle {
                        }
                }
                list.remove(thnStore);
-               if (thnStore.popup) {
-                       thnStore.send("" + (char) 33 + "You have fled from battle\n");
-               } else {
-                       thnStore.chatMessage("You have fled from battle");
-               }
+               thnStore.chatBattle("You have fled from battle");
                splitMoney(thnStore, (int) (thnStore.cash * engGame.gpfleemod), opponents);
                splitExp(thnStore, (int) (thnStore.exp * engGame.expfleemod), opponents);
                thnStore.leaveBattle();
@@ -358,11 +352,8 @@ public class Battle {
                if (money == 0) {
                        return;
                }
-               if (thnStore.popup) {
-                       thnStore.send("" + (char) 33 + "You have lost " + money + " gp.\n");
-               } else {
-                       thnStore.chatMessage("You have lost " + money + " gp.");
-               }
+               thnStore.chatBattle("You have lost " + money + " gp.");
+               // FIXME: atm's need transactions!
                thnStore.cash -= money;
                int i,
                                i2 = 0;
@@ -394,11 +385,7 @@ public class Battle {
        }
 
        void splitExp(LivingThing thnFront, int exp, ArrayList<LivingThing> opponents) {
-               if (thnFront.popup) {
-                       thnFront.send("" + (char) 33 + "You have lost " + exp + " exp.\n");
-               } else {
-                       thnFront.chatMessage("You have lost " + exp + " exp.");
-               }
+               thnFront.chatBattle("You have lost " + exp + " exp.");
                thnFront.exp -= exp;
                double tp,
                                sidepoints = 0;
@@ -446,32 +433,22 @@ public class Battle {
 
        void chatMessage(ArrayList<LivingThing> side1, ArrayList<LivingThing> side2, String strStore) {
                LivingThing thnStore = null;
-               String strStore2 = null;
-               if (!side2.isEmpty()) {
-                       thnStore = (LivingThing) side2.get(0);
-                       strStore2 = thnStore.name + " has " + thnStore.getCharacterPoints() + "cp and " + thnStore.hp + "/" + thnStore.maxhp + "hp.";
-               }
+               //String strStore2 = null;
+               //if (!side2.isEmpty()) {
+               //      thnStore = (LivingThing) side2.get(0);
+               //      strStore2 = thnStore.name + " has " + thnStore.getCharacterPoints() + "cp and " + thnStore.hp + "/" + thnStore.maxhp + "hp.";
+               //}
                for (int i = 0; i < side1.size(); i++) {
                        thnStore = (LivingThing) side1.get(i);
                        if (thnStore.isPlayer()) {
-                               if (thnStore.popup) {
-                                       if (strStore2 != null) {
-                                               thnStore.send(MessageType.UpdateBattle, strStore2 + "\n");
-                                               thnStore.send(MessageType.LogBattle, strStore + "\n");
-                                       }
-                               } else {
-                                       thnStore.chatMessage(strStore);
-                               }
+                               //thnStore.send(MessageType.UpdateBattle, strStore2 + "\n");
+                               //thnStore.send(MessageType.LogBattle, strStore + "\n");
+                               thnStore.chatBattle(strStore);
                        } else if (thnStore.isPet()) {
                                if (thnStore.getMaster().battle != thnStore.battle) {
-                                       if (thnStore.getMaster().popup) {
-                                               if (strStore2 != null) {
-                                                       thnStore.getMaster().send(MessageType.UpdateBattle, "From " + thnStore.name + ": " + strStore2 + "\n");
-                                                       thnStore.getMaster().send(MessageType.LogBattle, "From " + thnStore.name + ": " + strStore + "\n");
-                                               }
-                                       } else {
-                                               thnStore.chatMessage(strStore);
-                                       }
+                                       //thnStore.getMaster().send(MessageType.UpdateBattle, "From " + thnStore.name + ": " + strStore2 + "\n");
+                                       //thnStore.getMaster().send(MessageType.LogBattle, "From " + thnStore.name + ": " + strStore + "\n");
+                                       thnStore.chatBattle(strStore);
                                }
                        }
                }
@@ -482,13 +459,13 @@ public class Battle {
                chatMessage(vctSide2, vctSide1, strStore);
        }
 
-       void battleMessage(ArrayList<LivingThing> side1, MessageType type, String msg) {
+       void battleMessage(ArrayList<LivingThing> side1, ListMessage lm) {
                for (LivingThing thnStore : side1) {
                        if (thnStore.isPlayer()) {
-                               thnStore.send(type, msg);
+                               thnStore.send(lm);
                        } else if (thnStore.isPet()) {
                                if (thnStore.getMaster().battle != thnStore.battle) {
-                                       thnStore.send(type, msg);
+                                       thnStore.send(lm);
                                }
                        }
                }
@@ -500,14 +477,21 @@ public class Battle {
         * @param type
         * @param msg
         */
-       void battleMessage(MessageType type, String msg) {
-               battleMessage(vctSide1, type, msg);
-               battleMessage(vctSide2, type, msg);
+       void battleMessage(ListMessage msg) {
+               battleMessage(vctSide1, msg);
+               battleMessage(vctSide2, msg);
        }
 
        void hitMessage(long targetid, int delta, int targethp, int targettotalhp, long fromid, String what) {
-               battleMessage(MessageType.HitEntity,
-                               String.format("%d\n%d\n%d\n%d\n%d\n%s\n.\n", targetid, delta, targethp, targettotalhp, fromid, what));
+               ListMessage lm = new ListMessage(MSG_BATTLE_UPDATE);
+
+               lm.add(FIELD_BATTLE_TARGET, targetid);
+               lm.add(FIELD_BATTLE_DAMAGE, delta);
+               lm.add(FIELD_BATTLE_HP, targethp);
+               lm.add(FIELD_BATTLE_MAXHP, targettotalhp);
+               lm.add(FIELD_BATTLE_SOURCE, fromid);
+               lm.add(FIELD_BATTLE_WHAT, what);
+               battleMessage(lm);
        }
 
        /**
@@ -620,12 +604,8 @@ public class Battle {
                        updateFlags(vctSide2, thnFront2.ID, 0);
                        if (thnFront2.isPlayer()) {
                                thnFront2.removeFromGroup();
-                               chatMessage("\t" + thnFront2.name + " is killed.");
-                               if (thnFront2.popup) {
-                                       thnFront2.send("" + (char) 33 + "\tYou have died.\n");
-                               } else {
-                                       thnFront2.chatMessage("\tYou have died.");
-                               }
+                               chatMessage(thnFront2.name + " is killed.");
+                               thnFront2.chatBattle("You have died.");
                                splitMoney(thnFront2, (int) (thnFront2.cash * engGame.gplosemod), vctSide1);
                                splitExp(thnFront2, (int) (thnFront2.exp * engGame.explosemod), vctSide1);
                                thnFront2.leaveBattle();
@@ -650,7 +630,7 @@ public class Battle {
                                }
                        } else if (thnFront2.isMob()) {
                                Mob mob = (Mob) thnFront2;
-                               chatMessage("\t" + thnFront2.name + " is killed.");
+                               chatMessage(thnFront2.name + " is killed.");
                                splitMoney(thnFront2, (int) (thnFront2.cash), vctSide1);
                                splitExp(thnFront2, 0, vctSide1);
                                /**
@@ -691,8 +671,8 @@ public class Battle {
                                        }
                                }
                        } else if (thnFront2.isPet()) {
-                               chatMessage("\t" + thnFront2.name + " is wounded.");
-                               thnFront2.chatMessage("\tYou have been wounded.");
+                               chatMessage(thnFront2.name + " is wounded.");
+                               thnFront2.chatMessage("You have been wounded.");
                                splitMoney(thnFront2, (int) (thnFront2.cash * engGame.gplosemod), vctSide1);
                                splitExp(thnFront2, (int) (thnFront2.exp * engGame.explosemod), vctSide1);
                                thnFront2.leaveBattle();
diff --git a/DuskServer/src/duskz/server/BlockedIPException.java b/DuskServer/src/duskz/server/BlockedIPException.java
new file mode 100644 (file)
index 0000000..24aeffd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of DuskZ, a graphical mud engine.
+ *
+ * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
+ *
+ * DuskZ is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * DuskZ 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DuskZ; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+package duskz.server;
+
+/**
+ * IP is blocked
+ *
+ * @author Michael Zucchi <notzed@gmail.com>
+ */
+public class BlockedIPException extends Exception {
+
+       public BlockedIPException(String message) {
+               super(message);
+       }
+       
+}
index 01a790c..c28b57f 100644 (file)
 /**
  * Changes
  * Feb-2013 Michael Zucchi - modernised java
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server;
 
+import duskz.protocol.DuskProtocol;
 import duskz.server.entity.Mob;
 import duskz.server.entity.Merchant;
 import duskz.server.entity.Sign;
@@ -37,7 +39,7 @@ import java.io.*;
 import java.util.LinkedList;
 import java.util.StringTokenizer;
 
-public class Commands {
+public class Commands implements DuskProtocol {
 
        public static String parseCommand(LivingThing lt, DuskEngine game, String cmdline) throws Exception {
                if (cmdline == null) {
@@ -265,36 +267,38 @@ public class Commands {
                                // FIXME: this looks dodgy
                                // FIXME: move to livingthing
 
-                               thnStore.halt();
-                               thnStore.stillThere();  // This puts something in the buffer
-                               thnStore.stillThere();  // Have to do this twice to ensure that thnStore is out of
-                               // its read loop
-                               lt.connectionThread.sleep(500);  // wait for the "notdead" response to get back from client.
-                               try {
-                                       // Empty out the BufferedReader for the answer
-                                       //      while (thnStore.instream.ready()) {
-                                       //              thnStore.instream.read();
-                                       //      }
-                               } catch (Exception e) {
-                                       game.log.printError("parseCommand():While " + lt.name + " was trying to addmember " + thnStore.name, e);
-                               }
-                               thnStore.chatMessage(lt.name + " has invited you to join the clan " + lt.clan + ". If you accept, type yes.");
-                               try {
-                                       if (thnStore.instream.readLine().equalsIgnoreCase("yes")) {
-                                               thnStore.clan = lt.clan;
-                                               if (thnStore.privs == 1) {
-                                                       thnStore.privs = 0;
-                                               }
-                                               thnStore.chatMessage("You have been added to the clan, " + lt.clan + "");
-                                               thnStore.proceed();
-                                               game.removeDuskObject(thnStore);
-                                               game.addDuskObject(thnStore);
-                                               return thnStore.name + " has accepted your invitation.";
-                                       }
-                               } catch (Exception e) {
-                                       game.log.printError("parseCommand():While reading the answer to " + lt.name + "'s attempt to addmember " + thnStore.name, e);
-                               }
-                               thnStore.proceed();
+                               /*
+                                //             thnStore.halt();
+                                thnStore.stillThere();  // This puts something in the buffer
+                                thnStore.stillThere();  // Have to do this twice to ensure that thnStore is out of
+                                // its read loop
+                                lt.connectionThread.sleep(500);  // wait for the "notdead" response to get back from client.
+                                try {
+                                // Empty out the BufferedReader for the answer
+                                //     while (thnStore.instream.ready()) {
+                                //             thnStore.instream.read();
+                                //     }
+                                } catch (Exception e) {
+                                game.log.printError("parseCommand():While " + lt.name + " was trying to addmember " + thnStore.name, e);
+                                }
+                                thnStore.chatMessage(lt.name + " has invited you to join the clan " + lt.clan + ". If you accept, type yes.");
+                                try {
+                                if (thnStore.instream.readLine().equalsIgnoreCase("yes")) {
+                                thnStore.clan = lt.clan;
+                                if (thnStore.privs == 1) {
+                                thnStore.privs = 0;
+                                }
+                                thnStore.chatMessage("You have been added to the clan, " + lt.clan + "");
+                                thnStore.proceed();
+                                game.removeDuskObject(thnStore);
+                                game.addDuskObject(thnStore);
+                                return thnStore.name + " has accepted your invitation.";
+                                }
+                                } catch (Exception e) {
+                                game.log.printError("parseCommand():While reading the answer to " + lt.name + "'s attempt to addmember " + thnStore.name, e);
+                                }
+                                thnStore.proceed();
+                                */
                                return thnStore.name + " has declined your invitation.";
                        }
                        case "kick": {
@@ -508,78 +512,79 @@ public class Commands {
                                game.log.printMessage(Log.INFO, "godcommand:" + lt.name + ":" + cmdline + ":" + lt.x + "," + lt.y);
                                cmdline = cmdline.substring(5);
                                String filename = null;
-                               String strTitle = null;
+                               String title = null;
                                if (args.equals("items")) {
                                        filename = "defItems";
-                                       strTitle = "Items:\n";
+                                       title = "Items:\n";
                                } else if (args.equals("conf")) {
                                        filename = "conf";
-                                       strTitle = "Conf files:\n";
+                                       title = "Conf files:\n";
                                } else if (args.equals("mobs")) {
                                        filename = "defMobs";
-                                       strTitle = "Mobiles:\n";
+                                       title = "Mobiles:\n";
                                } else if (args.equals("commands")) {
                                        filename = "commands";
-                                       strTitle = "Custom commands:\n";
+                                       title = "Custom commands:\n";
                                } else if (args.equals("races")) {
                                        filename = "defRaces";
-                                       strTitle = "Races:\n";
+                                       title = "Races:\n";
                                } else if (args.equals("pets")) {
                                        filename = "defPets";
-                                       strTitle = "Pets:\n";
+                                       title = "Pets:\n";
                                } else if (args.equals("factions")) {
                                        filename = "factions";
-                                       strTitle = "Factions:\n";
+                                       title = "Factions:\n";
                                } else if (args.equals("conditions")) {
                                        filename = "defConditions";
-                                       strTitle = "Conditions:\n";
+                                       title = "Conditions:\n";
                                } else if (args.equals("help")) {
                                        filename = "helpFiles";
-                                       strTitle = "Help Files:\n";
+                                       title = "Help Files:\n";
                                } else if (args.equals("scripts")) {
                                        filename = "scripts";
-                                       strTitle = "Scripts:\n";
+                                       title = "Scripts:\n";
                                } else if (args.equals("spell groups")) {
                                        filename = "defSpellGroups";
-                                       strTitle = "Spell Groups:\n";
+                                       title = "Spell Groups:\n";
                                } else if (args.equals("spells")) {
                                        filename = "defSpells";
-                                       strTitle = "Spells:\n";
+                                       title = "Spells:\n";
                                } else if (args.equals("props")) {
                                        filename = "defProps";
-                                       strTitle = "Props:\n";
+                                       title = "Props:\n";
                                } else if (args.equals("move actions")) {
                                        filename = "defMoveActions";
-                                       strTitle = "Move Action Scripts:\n";
+                                       title = "Move Action Scripts:\n";
                                } else if (args.equals("can move")) {
                                        filename = "defCanMoveScripts";
-                                       strTitle = "Can Move Scripts:\n";
+                                       title = "Can Move Scripts:\n";
                                } else if (args.equals("can see")) {
                                        filename = "defCanSeeScripts";
-                                       strTitle = "Can See Scripts:\n";
+                                       title = "Can See Scripts:\n";
                                } else if (args.equals("tile actions")) {
                                        filename = "defTileActions";
-                                       strTitle = "Tile Action Scripts:\n";
+                                       title = "Tile Action Scripts:\n";
                                } else if (args.equals("tile move")) {
                                        filename = "defTileScripts";
-                                       strTitle = "Can Move Tile Scripts:\n";
+                                       title = "Can Move Tile Scripts:\n";
                                } else if (args.equals("tile see")) {
                                        filename = "defTileSeeScripts";
-                                       strTitle = "Tile See Scripts:\n";
+                                       title = "Tile See Scripts:\n";
                                }
                                if (filename != null) {
                                        File filList = new File(filename);
                                        String strResult[] = filList.list();
-                                       StringBuilder strBuff = new StringBuilder();
-                                       strBuff.append("").append((char) 20).append(strTitle).append("\n");
+                                       StringBuilder sb = new StringBuilder();
+                                       //strBuff.append("").append((char) 20).append(strTitle).append("\n");
                                        for (int i = 0; i < strResult.length; i++) {
                                                // Only output files that do not end in .dsko
                                                if (strResult[i].indexOf(".dsko") == -1) {
-                                                       strBuff.append(strResult[i]).append("\n");
+                                                       sb.append(strResult[i]).append("\n");
                                                }
                                        }
-                                       strBuff.append("--EOF--\n");
-                                       lt.send(strBuff.toString());
+                                       //strBuff.append("--EOF--\n");
+                                       //lt.send(strBuff.toString());
+                                       lt.viewText(title, false, sb.toString());
                                        return null;
                                }
                                return "You can't list that.";
@@ -658,24 +663,25 @@ public class Commands {
                                        if (blnPet) {
                                                return "The player named \"" + filView.getName() + "\" does not have a pet.";
                                        }
-                                       lt.send((char) 18 + args + "\n--EOF--\n");
+                                       //lt.send((char) 18 + args + "\n--EOF--\n");
+                                       lt.viewText(args, true, null);
                                        return null;
                                }
                                RandomAccessFile rafView = null;
-                               StringBuffer strBuff = new StringBuffer();
+                               StringBuilder sb = new StringBuilder();
                                try {
                                        rafView = new RandomAccessFile(filView, "rw");
                                        if (blnUser) {
                                                rafView.readLine();  //Skip over users' password
                                        }
                                        String strStore2 = rafView.readLine();
-                                       strBuff.append((char) 18 + args + "\n");
+                                       //sb.append((char) 18 + args + "\n");
                                        while (strStore2 != null) {
-                                               strBuff.append(strStore2 + "\n");
+                                               sb.append(strStore2 + "\n");
                                                strStore2 = rafView.readLine();
                                        }
-                                       strBuff.append("--EOF--\n");
-                                       lt.send(strBuff.toString());
+                                       //sb.append("--EOF--\n");
+                                       lt.viewText(args, true, sb.toString());
                                } catch (Exception e) {
                                        game.log.printError("parseCommand():Reading file for " + filView.getName(), e);
                                }
@@ -699,6 +705,12 @@ public class Commands {
                                if (args.indexOf("..") != -1) {
                                        return "You don't have permission to access that file.";
                                }
+
+                               if (true)
+                                       return "Developer hasn't implemented submit yet";
+
+                               // FIXME: implement submit, just use some submit message protocol
+
                                boolean compile = false;
                                boolean blnUser = false;
                                boolean blnPet = false;
@@ -798,11 +810,15 @@ public class Commands {
                                                 */
                                                rafView.writeBytes(cmdline + "\n");
                                        }
-                                       cmdline = lt.instream.readLine();
-                                       while (!cmdline.equals("--EOF--")) {
-                                               rafView.writeBytes(cmdline + "\n");
-                                               cmdline = lt.instream.readLine();
-                                       }
+                                       /**
+                                        * FIXME: from message
+                                        */
+                                       /*
+                                        cmdline = lt.instream.readLine();
+                                        while (!cmdline.equals("--EOF--")) {
+                                        rafView.writeBytes(cmdline + "\n");
+                                        cmdline = lt.instream.readLine();
+                                        }*/
                                        rafView.close();
                                        if (compile) {
                                                Script scrStore = new Script(filView.getPath(), game, true);
@@ -1307,6 +1323,10 @@ public class Commands {
                                if (args == null) {
                                        return "Change what?";
                                }
+
+                               if (true)
+                                       return "Race changing unimplemented";
+
                                if (args.equals("race")) {
                                        if (lt.getCharacterPoints() > game.changeRaceCpLimit) {
                                                return "You can no longer change your race.";
@@ -1314,40 +1334,41 @@ public class Commands {
                                        lt.unloadRace();
 
                                        // FIXME: I'm not sure why this needs to clear messages here.
+/*
+                                        if (lt.isPet()) {
 
-                                       if (lt.isPet()) {
-
-                                               lt.getMaster().halt();
-                                               //                                      lt.getMaster().stillThere();  // This puts something in the buffer
-                                               //                                      lt.getMaster().thrConnection.sleep(750);  // wait for it...
-                                               try {
-                                                       // Empty out the BufferedReader for the answer
-                                                       //      while (lt.getMaster().instream.ready()) {
-                                                       //              lt.getMaster().instream.readLine();
-                                                       //      }
-                                               } catch (Exception e) {
-                                                       game.log.printError("parseCommand():Trying to empty ready buffer of pet's master for change race.", e);
-                                               }
-                                       } else {
-                                               lt.halt();
-                                               //                                      lt.stillThere();  // This puts something in the buffer
-                                               //                                      lt.thrConnection.sleep(750);  // wait for it...
-                                               try {
-                                                       // Empty out the BufferedReader for the answer
-                                                       //      while (lt.instream.ready()) {
-                                                       //              lt.instream.readLine();
-                                                       //      }
-                                               } catch (Exception e) {
-                                                       game.log.printError("parseCommand():Trying to empty ready buffer of player for change race.", e);
-                                               }
-                                       }
-                                       lt.loadRace();
-                                       if (lt.isPet()) {
-                                               lt.getMaster().proceed();
-                                               lt.getMaster().updateStats();
-                                       } else {
-                                               lt.proceed();
-                                       }
+                                        lt.getMaster().halt();
+                                        //                                     lt.getMaster().stillThere();  // This puts something in the buffer
+                                        //                                     lt.getMaster().thrConnection.sleep(750);  // wait for it...
+                                        try {
+                                        // Empty out the BufferedReader for the answer
+                                        //     while (lt.getMaster().instream.ready()) {
+                                        //             lt.getMaster().instream.readLine();
+                                        //     }
+                                        } catch (Exception e) {
+                                        game.log.printError("parseCommand():Trying to empty ready buffer of pet's master for change race.", e);
+                                        }
+                                        } else {
+                                        lt.halt();
+                                        //                                     lt.stillThere();  // This puts something in the buffer
+                                        //                                     lt.thrConnection.sleep(750);  // wait for it...
+                                        try {
+                                        // Empty out the BufferedReader for the answer
+                                        //     while (lt.instream.ready()) {
+                                        //             lt.instream.readLine();
+                                        //     }
+                                        } catch (Exception e) {
+                                        game.log.printError("parseCommand():Trying to empty ready buffer of player for change race.", e);
+                                        }
+                                        }
+                                        lt.loadRace();
+                                        if (lt.isPet()) {
+                                        lt.getMaster().proceed();
+                                        lt.getMaster().updateStats();
+                                        } else {
+                                        lt.proceed();
+                                        }
+                                        */
                                        game.removeDuskObject(lt);
                                        game.addDuskObject(lt);
                                        lt.updateStats();
@@ -2248,27 +2269,35 @@ public class Commands {
                                if (args == null) {
                                        return "Unfollow who?";
                                }
+
+                               // FIXME: implemente unfollow
+                               if (true)
+                                       return "unfollow is not yet implemented";
+
+
                                LivingThing thnStore = lt.getFollowing();
                                if (thnStore != null && thnStore.isPet()) {
                                        if (thnStore.name.equalsIgnoreCase(args)) {
-                                               lt.halt();
-                                               lt.chatMessage("Do you really want to permanently erase your pet?");
-                                               try {
-                                                       if (lt.instream.readLine().equalsIgnoreCase("yes")) {
-                                                               lt.getFollowing().close();
-                                                               File deleteme = new File("pets/" + lt.name.toLowerCase());
-                                                               deleteme.delete();
-                                                               deleteme = new File("pets/" + lt.name.toLowerCase() + ".backup");
-                                                               deleteme.delete();
-                                                               lt.getFollowing().close();
-                                                               lt.setFollowing(lt.getFollowing().getFollowing());
-                                                               lt.proceed();
-                                                               return "Your pet has been erased.";
-                                                       }
-                                               } catch (Exception e) {
-                                                       game.log.printError("parseCommand():While unfollowing pet for " + lt.name, e);
-                                               }
-                                               lt.proceed();
+                                               /*
+                                                lt.halt();
+                                                lt.chatMessage("Do you really want to permanently erase your pet?");
+                                                try {
+                                                if (lt.instream.readLine().equalsIgnoreCase("yes")) {
+                                                lt.getFollowing().close();
+                                                File deleteme = new File("pets/" + lt.name.toLowerCase());
+                                                deleteme.delete();
+                                                deleteme = new File("pets/" + lt.name.toLowerCase() + ".backup");
+                                                deleteme.delete();
+                                                lt.getFollowing().close();
+                                                lt.setFollowing(lt.getFollowing().getFollowing());
+                                                lt.proceed();
+                                                return "Your pet has been erased.";
+                                                }
+                                                } catch (Exception e) {
+                                                game.log.printError("parseCommand():While unfollowing pet for " + lt.name, e);
+                                                }
+                                                lt.proceed();
+                                                * */
                                                return null;
                                        }
                                        thnStore = thnStore.getFollowing();
@@ -2309,7 +2338,11 @@ public class Commands {
                                if (lt.battle != null) {
                                        return "Wait until you're done battling.";
                                }
+                               // FIXME: reimplement unclan
+                               if (true)
+                                       return "unclan not implemented yet";
                                try {
+                                       /*
                                        lt.halt();
                                        lt.chatMessage("Are you sure you want to drop out of your clan? If so type yes.");
                                        if (lt.instream.readLine().equalsIgnoreCase("yes")) {
@@ -2321,11 +2354,11 @@ public class Commands {
                                                game.removeDuskObject(lt);
                                                game.addDuskObject(lt);
                                                return "You have been removed from your clan.";
-                                       }
+                                       }*/
                                } catch (Exception e) {
                                        game.log.printError("parseCommand():While " + lt.name + " was trying to dropout of their clan", e);
                                }
-                               lt.proceed();
+                               //lt.proceed();
                                return null;
                        }
 
@@ -2357,6 +2390,8 @@ public class Commands {
                                if (!lt.isPlayer()) {
                                        return "Only players can change their password.";
                                }
+                               return "password changing not re-implemented yet";
+                               /*
                                try {
                                        lt.halt();
                                        lt.chatMessage("Enter your current password.");
@@ -2384,6 +2419,7 @@ public class Commands {
                                        game.log.printError("parseCommand():While " + lt.name + " was changing their password", e);
                                }
                                lt.proceed();
+                               */
                        }
 
                        case "wear": {
@@ -2554,12 +2590,12 @@ public class Commands {
                                return "You are no longer ignoring " + strIgnoreName;
                        }
                        case "appletimages": {
-                               lt.updateAppletImages();
-                               return null;
+                               //lt.updateAppletImages();
+                               return "obsolete command";
                        }
                        case "applicationimages": {
-                               lt.updateApplicationImages();
-                               return null;
+                               //lt.updateApplicationImages();
+                               return "obsolete command";
                        }
                        case "notdead": {
                                return null;
index cd438ef..61eec18 100644 (file)
  * Changes
  * Feb-2013 Michael Zucchi - general cleanup, modernisation, refactoring,
  * abstracting, fixing synchronisation issues.
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server;
 
-import duskz.protocol.MessageType;
+import duskz.protocol.DuskMessage;
+import duskz.protocol.DuskProtocol;
 import duskz.server.entity.TileMap;
 import duskz.server.entity.Mob;
 import duskz.server.entity.Merchant;
@@ -35,11 +37,13 @@ import duskz.server.entity.Prop;
 import duskz.server.entity.DuskObject;
 import duskz.server.entity.PlayerMerchant;
 import duskz.server.entity.LivingThing;
-import duskz.protocol.ServerMessage;
 import duskz.protocol.TransactionItem;
+import duskz.protocol.TransactionMessage;
+import duskz.protocol.Wearing;
 import java.io.*;
 import java.io.PrintStream;
 import java.io.FileOutputStream;
+import java.net.Socket;
 import java.util.ArrayList;
 import java.util.StringTokenizer;
 import java.util.Date;
@@ -55,7 +59,7 @@ import java.util.Properties;
  *
  * @author Tom Weingarten
  */
-public class DuskEngine implements Runnable {
+public class DuskEngine implements Runnable, DuskProtocol {
        //Logger
 
        public Log log;
@@ -107,7 +111,7 @@ public class DuskEngine implements Runnable {
                        scrOnDeath = null,
                        scrOnLogOut = null;
        //End Prefs
-       public final static String version = "2.6.2.W42";
+       public final static String version = "3.0 dev";
        public final Date datStart = new Date(System.currentTimeMillis());
        //public int MapRows,
        //              MapColumns;
@@ -141,9 +145,14 @@ public class DuskEngine implements Runnable {
         */
        final private HashMap<String, Long> bannedIPMap = new HashMap<>();
        final public HashSet<LivingThing> checkConditionList = new HashSet<>();
+       // FIXME: This shouldn't be public
+       // FIXME: Requires synchronised access!
        public final HashMap<String, LivingThing> playersByName = new HashMap<>();
        VariableSet varVariables;
-       public VariableSet varIP;
+       /**
+        * Tracks login failures for a given address
+        */
+       final private HashMap<String, Integer> failureAddress = new HashMap<>();
        private long nextid = 0;
        boolean blnVariableListChanged = false,
                        blnMapHasChanged = false,
@@ -155,7 +164,6 @@ public class DuskEngine implements Runnable {
        public DuskEngine() {
                RandomAccessFile rafFile = null;
                varVariables = new VariableSet();
-               varIP = new VariableSet();
                log = new Log(System.out);
 
                try {
@@ -524,7 +532,7 @@ public class DuskEngine implements Runnable {
                        oldviewrange = viewrange;
                        mapsize = 1 + (2 * viewrange);
                        for (LivingThing thnStore : playersByName.values()) {
-                               thnStore.resizeMap();
+                               thnStore.initMap();
                        }
                }
                //Load Triggered Scripts
@@ -734,8 +742,8 @@ public class DuskEngine implements Runnable {
                                        if (o.isPlayerMerchant()) {
                                                PlayerMerchant shop = (PlayerMerchant) o;
                                                if (refresh.x == shop.x && refresh.y == shop.y) {
-                                                       //strResult = (char) 17 + "";
-                                                       StringBuilder sb = new StringBuilder();
+                                                       TransactionMessage tm = new TransactionMessage(MSG_UPDATE_MERCHANT);
+
                                                        for (String name : shop.vctItems.keySet()) {
                                                                LinkedList<Item> vctStore = shop.vctItems.get(name);
                                                                Item item = (Item) vctStore.element();
@@ -744,83 +752,54 @@ public class DuskEngine implements Runnable {
                                                                if (refresh.name.equalsIgnoreCase(shop.strOwner)) {
                                                                        intCost = 0;
                                                                }
-                                                               sb.append(intCost + "gp)" + name + "\n");
+                                                               tm.add(item.getWearLocation(), name, vctStore.size(), intCost, "gp");
                                                        }
-                                                       sb.append(".\n");
-                                                       refresh.send(MessageType.UpdateMerchant, sb.toString());
-                                                       refresh.updateSell();
+                                                       refresh.send(tm);
+                                                       refresh.updateInventory();
                                                }
                                        }
                                        if (o.isMerchant()) {
                                                Merchant merchant = (Merchant) o;
                                                if (refresh.x == merchant.x && refresh.y == merchant.y) {
-                                                       if (true) {
-                                                               StringBuilder sb = new StringBuilder();
-                                                               //strResult = (char) 17 + "";
-                                                               if (refresh.getFollowing() != null && refresh.getFollowing().isPet()) {
-                                                                       for (int i5 = 0; i5 < merchant.items.size(); i5++) {
-                                                                               String itemname = (String) merchant.items.get(i5);
-                                                                               Item item = getItem(itemname);
-                                                                               if (item != null) {
-                                                                                       sb.append(item.intCost).append("gp)").append(itemname).append("\n");
-                                                                               } else if (itemname.equals("pet")) {
-                                                                                       sb.append(petcost).append("gp)").append(itemname).append("\n");
-                                                                               } else {
-                                                                                       sb.append(traincost).append("exp)").append(itemname).append("\n");
-                                                                                       sb.append(traincost).append("exp)").append(refresh.getFollowing().name).append(":").append(itemname).append("\n");
+                                                       HashMap<String, TransactionItem> items = new HashMap<>();
+                                                       for (String itemname : merchant.items) {
+                                                               TransactionItem titem = items.get(itemname);
+                                                               if (titem == null) {
+                                                                       Item item = getItem(itemname);
+                                                                       if (item != null) {
+                                                                               titem = new TransactionItem(item.getWearLocation(), itemname, 1, item.intCost, "gp");
+                                                                       } else if (itemname.equals("pet")) {
+                                                                               // FIXME: pet?
+                                                                               titem = new TransactionItem(Wearing.INVENTORY, itemname, 1, petcost, "gp");
+                                                                       } else {
+                                                                               if (refresh.getFollowing() != null && refresh.getFollowing().isPet()) {
+                                                                                       // FIXME: pet skill?
+                                                                                       titem = new TransactionItem(Wearing.INVENTORY, refresh.getFollowing().name + ":" + itemname, 1, traincost, "exp");
+                                                                                       items.put(titem.name, titem);
                                                                                }
+                                                                               // FIXME: Wearing.skill?
+                                                                               titem = new TransactionItem(Wearing.INVENTORY, itemname, 1, traincost, "exp");
+                                                                               //sb.append(traincost).append("exp)").append(itemname).append("\n");
+                                                                               //sb.append(traincost).append("exp)").append(refresh.getFollowing().name).append(":").append(itemname).append("\n");
                                                                        }
+                                                                       items.put(titem.name, titem);
                                                                } else {
-                                                                       for (int i5 = 0; i5 < merchant.items.size(); i5++) {
-                                                                               String itemname = (String) merchant.items.get(i5);
-                                                                               Item item = getItem(itemname);
-                                                                               if (item != null) {
-                                                                                       sb.append(item.intCost).append("gp)").append(itemname).append("\n");
-                                                                               } else if (itemname.equals("pet")) {
-                                                                                       sb.append(petcost).append("gp)").append(itemname).append("\n");
-                                                                               } else {
-                                                                                       sb.append(traincost).append("exp)").append(itemname).append("\n");
-                                                                               }
-                                                                       }
-                                                               }
-                                                               sb.append(".\n");
-                                                               refresh.send(MessageType.UpdateMerchant, sb.toString());
-                                                               refresh.updateSell();
-                                                       } else {
-                                                               HashMap<String, TransactionItem> items = new HashMap<>();
-                                                               for (String itemname : merchant.items) {
-                                                                       TransactionItem titem = items.get(itemname);
-                                                                       if (titem == null) {
-                                                                               Item item = getItem(itemname);
-                                                                               if (item != null) {
-                                                                                       titem = new TransactionItem(itemname, 1, item.intCost, "gp");
-                                                                               } else if (itemname.equals("pet")) {
-                                                                                       titem = new TransactionItem(itemname, 1, petcost, "gp");
-                                                                               } else {
-                                                                                       if (refresh.getFollowing() != null && refresh.getFollowing().isPet()) {
-                                                                                               titem = new TransactionItem(refresh.getFollowing().name + ":" + itemname, 1, traincost, "exp");
-                                                                                               items.put(titem.name, titem);
-                                                                                       }
-                                                                                       titem = new TransactionItem(itemname, 1, traincost, "exp");
-                                                                                       //sb.append(traincost).append("exp)").append(itemname).append("\n");
-                                                                                       //sb.append(traincost).append("exp)").append(refresh.getFollowing().name).append(":").append(itemname).append("\n");
-                                                                               }
-                                                                               items.put(titem.name, titem);
-                                                                       } else {
-                                                                               titem.count++;
-                                                                       }
+                                                                       titem.count++;
                                                                }
-                                                               refresh.send(new ServerMessage.ItemsMessage(MessageType.UpdateMerchant, new ArrayList<>(items.values())));
-                                                               refresh.updateSell();
                                                        }
+
+                                                       TransactionMessage tm = new TransactionMessage(MSG_UPDATE_MERCHANT);
+                                                       tm.items.addAll(items.values());
+                                                       refresh.send(tm);
+                                                       refresh.updateInventory();
                                                }
-                                               //if (old == null) { //i4 != -1
-                                               //      if (!objStore.isLivingThing()
-                                               //                      || canSeeLivingThing(thnRefresh, (LivingThing) objStore)) {
-                                               //              thnRefresh.send(MessageType.AddEntity, objStore.toEntity());
-                                               //      }
-                                               //}
                                        }
+                                       //if (old == null) { //i4 != -1
+                                       //      if (!objStore.isLivingThing()
+                                       //                      || canSeeLivingThing(thnRefresh, (LivingThing) objStore)) {
+                                       //              thnRefresh.send(MessageType.AddEntity, objStore.toEntity());
+                                       //      }
+                                       //}
                                }
                        }
                }
@@ -1430,7 +1409,7 @@ public class DuskEngine implements Runnable {
        synchronized void resizeMap(int x, int y) {
                map.resize(x, y);
                for (LivingThing lt : playersByName.values()) {
-                       lt.resizeMap();
+                       lt.initMap();
                }
                blnMapHasChanged = true;
        }
@@ -1728,7 +1707,7 @@ public class DuskEngine implements Runnable {
         * @param dir
         */
        // FIXME: now i think this probably needs to be moved back to livingthing ...
-       public void moveDuskObject(LivingThing thing, int inlocx, int inlocy, MessageType dir) {
+       public void moveDuskObject(LivingThing thing, int inlocx, int inlocy, byte dir) {
                for (TileMap.MapData md : map.range(thing.x, thing.y, viewrange)) {
                        for (DuskObject o : md.entities) {
                                if (o.isLivingThing()) {
@@ -1741,7 +1720,7 @@ public class DuskEngine implements Runnable {
                                                if (canSee) {
                                                        // Add/update it if now visible
                                                        lt.addEntity(thing);
-                                                       lt.send(dir, thing.ID + "\n");
+                                                       lt.send(DuskMessage.create(thing.ID, MSG_MOVE, dir));
                                                } else {
                                                        // Remove it if it isn't
                                                        lt.removeEntity(thing.ID);
@@ -2042,4 +2021,68 @@ public class DuskEngine implements Runnable {
                        bannedIPMap.clear();;
                }
        }
+
+       /**
+        * Log/track password failures
+        *
+        * @param name name of user who failed
+        * @param address address of failure
+        * @return true if the host is now banned
+        */
+       public boolean passwordFailure(String name, String address) {
+               log.printMessage(Log.INFO, address + ":" + name + " entered the wrong password");
+               Integer failCount = failureAddress.get(address);
+               if (failCount != null) {
+                       int fc = failCount.intValue();
+                       if (fc >= 4) {
+                               banAddress(address);
+                               return true;
+                       } else {
+                               failureAddress.put(address, fc + 1);
+                       }
+               } else {
+                       failureAddress.put(address, 1);
+               }
+
+               return false;
+       }
+
+       public void passwordSuccess(String name, String address) {
+               failureAddress.remove(address);
+       }
+
+       /**
+        * This registers a new player atomically.
+        *
+        * Any existing player with the same name is booted
+        */
+       public void registerPlayer(LivingThing lt, String name) throws BlockedIPException {
+               synchronized (playersByName) {
+                       // Check ip filter, FIXME: test
+                       if (blnIPF) {
+                               String address = lt.getAddress();
+
+                               for (LivingThing thing : playersByName.values()) {
+                                       if (thing.getAddress().equalsIgnoreCase(address)) {
+                                               throw new BlockedIPException("Already a player connected from your address");
+                                       }
+                               }
+                       }
+
+                       LivingThing thing;
+                       name = name.toLowerCase();
+                       thing = playersByName.get(name);
+
+                       if (thing != null) {
+                               log.printMessage(Log.INFO, lt.getAddress() + ":" + name + " tried to log in twice");
+                               // FIXME: nowhere to send this
+                               //chatMessage("That user is already logged in. They are being logged out.");
+                               lt.chatMessage("There has been another logon under this name, you are being logged out.");
+                               lt.close();
+
+                       }
+
+                       playersByName.put(name, lt);
+               }
+       }
 }
index 7e9dc6e..fe0e5fb 100644 (file)
@@ -1710,10 +1710,11 @@ public class Script {
                                        }
                                        if (true)
                                                // FIXME: protocol implementation
-                                               throw new RuntimeException("cannot ask questions yet");
+                                               throw new RuntimeException("unimplementeD: cannot ask questions yet");
                                        // FIXME: this looks dodgy
                                        // FIXME: move to livingthing
-                                       thnStore.halt();
+                                       // FIXME: needs command query thing
+                                       //      thnStore.halt();
                                        thnStore.stillThere();  // This puts something in the buffer
                                        thnStore.stillThere();  // Need to do this twice to ensure thnStore out of read loop
                                        thnStore.connectionThread.sleep(750);  // wait for it...
@@ -1725,9 +1726,9 @@ public class Script {
                                        } catch (Exception e) {
                                                engGame.log.printError("parseScript():While trying to empty read buffer to get player input", e);
                                        }
-                                       strInput = thnStore.instream.readLine();
+                                       //      strInput = thnStore.instream.readLine();
                                        varVariables.addVariable(getString(), strInput);
-                                       thnStore.proceed();
+                                       //      thnStore.proceed();
                                        return true;
                                }
                                case 49: {
@@ -1759,20 +1760,23 @@ public class Script {
                                        return true;
                                }
                                case 52: {
-                                       LivingThing thnStore = getLivingThing(getString());
-                                       String strTitle = getString();
-                                       String strStore2 = getString();
-                                       String strLine = null;
-                                       int intIndex = strStore2.indexOf("\n");
-                                       thnStore.send((char) 20 + strTitle + "\n");
-                                       while (intIndex != -1) {
-                                               strLine = strStore2.substring(0, intIndex);
-                                               thnStore.send(strLine + "\n");
-                                               strStore2 = strStore2.substring(intIndex + 1);
-                                               intIndex = strStore2.indexOf("\n");
-                                       }
-                                       thnStore.send(strStore2 + "\n");
-                                       thnStore.send("--EOF--\n");
+                                       // FIXME: This is the VIEW_TEXT command
+                                       /*
+                                        LivingThing thnStore = getLivingThing(getString());
+                                        String strTitle = getString();
+                                        String strStore2 = getString();
+                                        String strLine = null;
+                                        int intIndex = strStore2.indexOf("\n");
+                                        thnStore.send((char) 20 + strTitle + "\n");
+                                        while (intIndex != -1) {
+                                        strLine = strStore2.substring(0, intIndex);
+                                        thnStore.send(strLine + "\n");
+                                        strStore2 = strStore2.substring(intIndex + 1);
+                                        intIndex = strStore2.indexOf("\n");
+                                        }
+                                        thnStore.send(strStore2 + "\n");
+                                        thnStore.send("--EOF--\n");
+                                        * */
                                        return true;
                                }
                                case 53: {
@@ -1842,23 +1846,25 @@ public class Script {
                                        return true;
                                }
                                case 58: {
-                                       LivingThing thnStore = getLivingThing(getString());
-                                       String strStore2 = getString();
-                                       int intIndex = strStore2.indexOf("\n");
-                                       while (intIndex != -1) {
-                                               if (thnStore.battle != null && thnStore.popup) {
-                                                       thnStore.send("" + (char) 33 + strStore2.substring(0, intIndex + 1));
-                                               } else {
-                                                       thnStore.chatMessage(strStore2.substring(0, intIndex));
-                                               }
-                                               strStore2 = strStore2.substring(intIndex + 1);
-                                               intIndex = strStore2.indexOf("\n");
-                                       }
-                                       if (thnStore.battle != null && thnStore.popup) {
-                                               thnStore.send("" + (char) 33 + strStore2 + "\n");
-                                       } else {
-                                               thnStore.chatMessage(strStore2);
-                                       }
+                                       // FIXME: battle chat
+                                       /*
+                                        LivingThing thnStore = getLivingThing(getString());
+                                        String strStore2 = getString();
+                                        int intIndex = strStore2.indexOf("\n");
+                                        while (intIndex != -1) {
+                                        if (thnStore.battle != null && thnStore.popup) {
+                                        thnStore.send("" + (char) 33 + strStore2.substring(0, intIndex + 1));
+                                        } else {
+                                        thnStore.chatMessage(strStore2.substring(0, intIndex));
+                                        }
+                                        strStore2 = strStore2.substring(intIndex + 1);
+                                        intIndex = strStore2.indexOf("\n");
+                                        }
+                                        if (thnStore.battle != null && thnStore.popup) {
+                                        thnStore.send("" + (char) 33 + strStore2 + "\n");
+                                        } else {
+                                        thnStore.chatMessage(strStore2);
+                                        }*/
                                        return true;
                                }
                                case 32: {
index e853cc3..1aa021c 100644 (file)
@@ -101,4 +101,30 @@ public class SpellGroup {
                }
                return sb.toString();
        }
+
+       public List<String> spellList(List<String> list, int percent) {
+               String name;
+
+               if (list == null)
+                       list = new ArrayList<>();
+
+               if (vctSpells.isEmpty()) {
+                       return list;
+               }
+
+               name = (String) vctSpells.get(0);
+               if (name == null) {
+                       return list;
+               }
+               list.add(((110 - percent) / 2) + " mp) " + name);
+               for (int i = 1; i < vctSpells.size(); i++) {
+                       if (percent < (100 * i) / (vctSpells.size() - 1)) {
+                               break;
+                       }
+                       name = (String) vctSpells.get(i);
+
+                       list.add(((110 - (percent - (100 * i) / (vctSpells.size() - 1))) / 2) + " mp) " + name);
+               }
+               return list;
+       }
 }
\ No newline at end of file
index fb48dc3..92e413f 100644 (file)
  * Changes
  * Feb-2013 Michael Zucchi - modernised java, added list functions and
  * serialisation..
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server.entity;
 
-import duskz.protocol.MessageType;
-import duskz.protocol.ServerMessage;
-import duskz.protocol.ServerMessage.EntityMessage;
+import duskz.protocol.EntityUpdateMessage;
 
 /*
  All code copyright Tom Weingarten (captaint@home.com) 2000
@@ -122,9 +121,19 @@ public abstract class DuskObject {
                return sb.toString();
        }
 
-       public EntityMessage toMessage(MessageType type) {
-               return new ServerMessage.EntityMessage(type, ID, name,
-                               (byte) getEntityType(), (short) x, (short) y, (short) getImage(), (short) -1);
+       public EntityUpdateMessage toMessage(int msg) {
+               EntityUpdateMessage en = new EntityUpdateMessage();
+               
+               en.name = msg;
+               en.id = ID;
+               en.entityName = name;
+               en.entityType = (byte)getEntityType();
+               en.x = (short)x;
+               en.y = (short)y;
+               en.image = (short) getImage();
+               en.imageStep = -1;
+               
+               return en;
        }
 
        // Linked list stuff - should it just use a container?
index 5eb7d78..f26fb1c 100644 (file)
 /**
  * Changes
  * Feb-2013 Michael Zucchi - modernised java, big refactor.
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server.entity;
 
+import duskz.protocol.DuskMessage;
+import duskz.protocol.ListMessage;
 import duskz.protocol.Wearing;
 
 /**
@@ -60,6 +63,18 @@ public class Equipment implements Wearing {
                return sb.toString();
        }
 
+       public DuskMessage toMessage(int msgid) {
+               ListMessage msg = new ListMessage(msgid);
+
+               for (int i = 0; i < worn.length; i++) {
+                       Item item = worn[i];
+                       if (item != null)
+                               msg.add(i, item.name);
+               }
+
+               return msg;
+       }
+
        public Item getWorn(int where) {
                return worn[where];
        }
index b48901e..66fa8a7 100644 (file)
@@ -25,6 +25,7 @@
  */
 package duskz.server.entity;
 
+import duskz.protocol.Wearing;
 import duskz.server.DuskEngine;
 import duskz.server.Script;
 import java.io.File;
@@ -147,6 +148,24 @@ public class Item extends DuskObject {
                return 1;
        }
 
+       /**
+        * type of kind as from Wearing
+        *
+        * @return
+        */
+       public int getWearLocation() {
+               int kind;
+
+               if (isArmor()) {
+                       kind = 1 + intKind;
+               } else if (isWeapon()) {
+                       kind = Wearing.WIELD;
+               } else {
+                       kind = Wearing.INVENTORY;
+               }
+               return kind;
+       }
+
        /*
         **     This method formats the Item for saving.
         **     It generates a String that can later be passed
index 4c5e9f6..90d3fb5 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  * This file is part of DuskZ, a graphical mud engine.
  *
  * Copyright (C) 2000 Tom Weingarten <captaint@home.com>
  * Changes
  * Feb-2013 Michael Zucchi - modernised java, lots of clean up and encapsulation
  * and synchronisation fixes. Moved loader here.
+ * Mar-2013 Michael Zucchi - changed server protocol
  */
 package duskz.server.entity;
 
+import duskz.protocol.DuskMessage;
+import duskz.protocol.DuskMessage.EntityMessage;
+import duskz.protocol.DuskMessage.StringMessage;
+import duskz.protocol.DuskProtocol;
+import static duskz.protocol.DuskProtocol.AUTH_LOGIN_FAILED;
+import static duskz.protocol.DuskProtocol.FIELD_AUTH_REASON;
+import static duskz.protocol.DuskProtocol.FIELD_AUTH_RESULT;
+import duskz.protocol.EntityListMessage;
+import duskz.protocol.EntityUpdateMessage;
+import duskz.protocol.ListMessage;
+import duskz.protocol.MapMessage;
+import duskz.protocol.TransactionMessage;
 import duskz.server.Battle;
+import duskz.server.BlockedIPException;
 import duskz.server.Commands;
 import duskz.server.Condition;
 import duskz.server.DuskEngine;
 import duskz.server.ItemList;
 import duskz.server.Log;
-import duskz.protocol.MessageType;
 import duskz.server.Script;
 import duskz.server.SpellGroup;
-import duskz.server.Variable;
 import duskz.server.entity.TileMap.MapData;
 import duskz.server.entity.TileMap.MoveData;
-import duskz.protocol.ServerMessage;
 import java.io.*;
 import java.net.Socket;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -57,10 +73,10 @@ import java.util.logging.Logger;
  *
  * @author Tom Weingarten
  */
-public class LivingThing extends DuskObject implements Runnable {
+public class LivingThing extends DuskObject implements Runnable, DuskProtocol {
 
        DuskEngine game;
-       final public LinkedBlockingQueue<ServerMessage> messageQueue = new LinkedBlockingQueue<>();
+       final public LinkedBlockingDeque<DuskMessage> messageQueue = new LinkedBlockingDeque<>();
        //StillWorking?
        public boolean isWorking = true;
        //Has the player finished loading yet?
@@ -167,9 +183,8 @@ public class LivingThing extends DuskObject implements Runnable {
        //Socket connection
        private Socket socket;
        // FIXME: not public
-       public DataInputStream instream;
-       // FIXME: not public
-       public DataOutputStream outstream;
+       private DataInputStream instream;
+       private DataOutputStream outstream;
        public Thread connectionThread;
        private SendThread sendThread;
        //Prefs
@@ -231,11 +246,16 @@ public class LivingThing extends DuskObject implements Runnable {
        }
 
        /**
-        * Get IP address of user
+        * Get IP address of player
         *
         * @return
         */
        public String getAddress() {
+               // TODO: the connect code had the following, why?
+               //              String strIP = socket.getInetAddress().toString();
+               //int ip = strIP.indexOf("/");
+               //strIP = strIP.substring(ip + 1, strIP.length());
+
                if (socket != null
                                && socket.isConnected())
                        return socket.getInetAddress().toString();
@@ -275,7 +295,9 @@ public class LivingThing extends DuskObject implements Runnable {
        }
 
        @Override
-       public ServerMessage.EntityMessage toMessage(MessageType type) {
+       public EntityUpdateMessage toMessage(int msg) {
+               EntityUpdateMessage en = super.toMessage(msg);
+
                StringBuilder sb = new StringBuilder();
                if (isSleeping) {
                        sb.append("<sleeping>");
@@ -294,8 +316,12 @@ public class LivingThing extends DuskObject implements Runnable {
                        sb.append('>');
                }
                sb.append(name);
-               return new ServerMessage.EntityMessage(type, ID, sb.toString(),
-                               (byte) getEntityType(), (short) x, (short) y, (short) getImage(), (short) (isPlayer() ? imagestep : -1));
+
+               en.entityName = sb.toString();
+               if (isPlayer())
+                       en.imageStep = (short) imagestep;
+
+               return en;
        }
 
        /**
@@ -329,7 +355,7 @@ public class LivingThing extends DuskObject implements Runnable {
 
                        if (game.maxconnections != 0 && game.playersByName.size() >= game.maxconnections) {
                                chatMessage("Sorry, the server has reached it's connection limit. Try again later.");
-                               send("Goodbye.\n" + (char) 0);
+                               quit();
                                Thread.sleep(1000);
                                closeNosavePlayer();
                                return;
@@ -337,32 +363,18 @@ public class LivingThing extends DuskObject implements Runnable {
                        // Game is shutting down
                        if (game.blnShuttingDown) {
                                chatMessage("Sorry, the server is not accepting new connections. It is either being shutdown or worked on.  Try again later.");
-                               send("Goodbye.\n" + (char) 0);
+                               quit();
                                Thread.sleep(1000);
                                closeNosavePlayer();
                                return;
                        }
                        if (!game.isGoodIP(socket.getInetAddress().toString())) {
                                chatMessage("Connections from your machine are no longer being accepted.");
-                               send("Goodbye.\n" + (char) 0);
+                               quit();
                                Thread.sleep(1000);
                                closeNosavePlayer();
                                return;
                        }
-                       // IP Filter FIXME: put on engine
-                       if (game.blnIPF) {
-                               for (LivingThing lt : game.playersByName.values()) {
-                                       String IP = lt.socket.getInetAddress().toString();
-                                       if (IP.equalsIgnoreCase(socket.getInetAddress().toString())) {
-                                               chatMessage("There's already a player connected from your IP address.");
-                                               send("Goodbye.\n" + (char) 0);
-                                               Thread.sleep(1000);
-                                               closeNosavePlayer();
-                                               return;
-                                       }
-                               }
-                       }
-                       // End IPF
                } catch (Exception e) {
                        game.log.printError("LivingThing():Creating player", e);
                }
@@ -404,13 +416,13 @@ public class LivingThing extends DuskObject implements Runnable {
                                        return;
                                }
                        }
-                       loadUserFile(new File("pets/default"));
+                       loadUserFile(new File("pets/default"), false);
                        strStore = this.master.name.toLowerCase();
                        File filPlayer = new File("pets/" + strStore);
                        File filBackup = new File("pets/" + strStore + ".backup");
                        /*
-                        ** Check for old style pet name
-                        ** (Only need to check if we already have a valid pet name)
+                        ** Check for old style pet entityName
+                        ** (Only need to check if we already have a valid pet entityName)
                         */
                        if (!strName.equalsIgnoreCase("default")) {
                                strStore = strName.toLowerCase();
@@ -449,7 +461,7 @@ public class LivingThing extends DuskObject implements Runnable {
 
                        file = new File("pets", strStore);
                        backup = new File("pets", strStore + ".backup");
-                       loadUserFile(file);
+                       loadUserFile(file, false);
                        if (race != null) {
                                loadRaceFile(new File("defPets/" + race.toLowerCase()), true);
                        }
@@ -520,7 +532,7 @@ public class LivingThing extends DuskObject implements Runnable {
        /**
         * Train a skill or spell.
         *
-        * @param name
+        * @param entityName
         * @param add increment
         * @return true if the skill/spell exists and was created/incremented
         */
@@ -591,14 +603,20 @@ public class LivingThing extends DuskObject implements Runnable {
                return total;
        }
 
-       public void loadUserFile(File path) throws FileNotFoundException, IOException {
-               String line;
+       public void loadUserFile(File path, boolean haspass) throws FileNotFoundException, IOException {
                try (RandomAccessFile file = new RandomAccessFile(path, "r")) {
+                       String line;
+                       int lineNo = 1;
+                       if (haspass) {
+                               line = file.readLine();
+                               lineNo++;
+                       }
                        while (!((line = file.readLine()) == null || line.equals("."))) {
                                try {
                                        parseUserFile(file, line);
+                                       lineNo += 1;
                                } catch (NumberFormatException x) {
-                                       throw new IOException("Problem parsing user " + path + " on field " + line, x);
+                                       throw new IOException("Problem parsing user " + path + " line: " + lineNo + " `" + line + "'", x);
                                }
                        }
                }
@@ -791,7 +809,7 @@ public class LivingThing extends DuskObject implements Runnable {
                                coloron = false;
                                break;
                }
-               //      engGame.log.printError("parseUserFile():Parsing \"" + strStore + "\" from " + name + "'s file", e);
+               //      engGame.log.printError("parseUserFile():Parsing \"" + strStore + "\" from " + entityName + "'s file", e);
        }
 
        public void loadRaceFile(File path, boolean add) throws FileNotFoundException, IOException {
@@ -1013,7 +1031,7 @@ public class LivingThing extends DuskObject implements Runnable {
                 }
 
                 if (!blnSaveSuccessful || (lngFileLength < 100)) {
-                engGame.log.printMessage(Log.ERROR, "savePlayer():Saving primary file for " + name + ", second try failed, aborting savePlayer");
+                engGame.log.printMessage(Log.ERROR, "savePlayer():Saving primary file for " + entityName + ", second try failed, aborting savePlayer");
                 blnCanSave = true;
                 return;
                 }
@@ -1047,7 +1065,7 @@ public class LivingThing extends DuskObject implements Runnable {
                 }
 
                 if (!blnSaveSuccessful || (lngFileLength < 100)) {
-                engGame.log.printMessage(Log.ERROR, "savePlayer():Saving backup file for " + name + ", second try failed, aborting savePlayer");
+                engGame.log.printMessage(Log.ERROR, "savePlayer():Saving backup file for " + entityName + ", second try failed, aborting savePlayer");
                 blnCanSave = true;
                 return;
                 }
@@ -1056,6 +1074,7 @@ public class LivingThing extends DuskObject implements Runnable {
                isSaveable = true;
        }
 
+       // TODO: the close commands need merging somehow
        public void close() {
                if (name == null || name.equalsIgnoreCase("default")) {
                        return;
@@ -1234,6 +1253,7 @@ public class LivingThing extends DuskObject implements Runnable {
                isWorking = false;
                isSaveable = false;
                isSaveNeeded = false;
+
                if (!conditions.isEmpty() && game.checkConditionList.contains(this)) {
                        game.checkConditionList.remove(this);
                }
@@ -1287,12 +1307,17 @@ public class LivingThing extends DuskObject implements Runnable {
                }
        }
 
+       // TODO: shouldn' this work on 'this.ID'?
        public void updateFlag(long ID, int Value) {
-               send((char) 29 + "" + ID + "\n" + Value + "\n");
+               ListMessage lm = new EntityListMessage(ID, MSG_UPDATE_ENTITY);
+
+               lm.add(FIELD_ENTITY_FLAGS, Value);
+
+               send(lm);
        }
 
        public void clearFlags() {
-               send((char) 30 + "");
+               send(DuskMessage.create(MSG_CLEAR_FLAGS));
        }
 
        public boolean hasPendingMoves() {
@@ -1396,7 +1421,7 @@ public class LivingThing extends DuskObject implements Runnable {
        }
 
        // FIXME: should probably be in DuskEngine
-       protected synchronized void moveTo(int newLocX, int newLocY, MessageType dir, int intNewStep) {
+       protected synchronized void moveTo(int newLocX, int newLocY, byte dir, int intNewStep) {
                if (privs < 5 && (newLocX >= (game.map.getCols() - 1)
                                || newLocY >= (game.map.getRows() - 1)
                                || newLocX < 0
@@ -1487,19 +1512,19 @@ public class LivingThing extends DuskObject implements Runnable {
        }
 
        public void moveN() {
-               moveTo(x, y - 1, MessageType.MoveNorth, 0);
+               moveTo(x, y - 1, (byte) 'n', 0);
        }
 
        public void moveS() {
-               moveTo(x, y + 1, MessageType.MoveSouth, 2);
+               moveTo(x, y + 1, (byte) 's', 2);
        }
 
        public void moveW() {
-               moveTo(x - 1, y, MessageType.MoveWest, 4);
+               moveTo(x - 1, y, (byte) 'w', 4);
        }
 
        public void moveE() {
-               moveTo(x + 1, y, MessageType.MoveEast, 6);
+               moveTo(x + 1, y, (byte) 'e', 6);
        }
 
        public DuskObject removeEntity(long id) {
@@ -1508,7 +1533,7 @@ public class LivingThing extends DuskObject implements Runnable {
 
                        if (o != null) {
                                System.out.println("Removing client entity " + id + " from " + name);
-                               send(MessageType.RemoveEntity, id + "\n");
+                               send(EntityMessage.create(id, MSG_REMOVE_ENTITY));
                        }
                        return o;
                }
@@ -1535,7 +1560,7 @@ public class LivingThing extends DuskObject implements Runnable {
                                || game.canSeeLivingThing(this, (LivingThing) ent)) {
                        System.out.println("Adding entity " + ent.name + " to " + name);
                        //send(MessageType.AddEntity, ent.toEntity());
-                       send(ent.toMessage(MessageType.AddEntity));
+                       send(ent.toMessage(MSG_ADD_ENTITY));
                } else {
                        System.out.println("Not adding entity " + ent.name + " to " + name);
                }
@@ -1651,7 +1676,9 @@ public class LivingThing extends DuskObject implements Runnable {
 
                int r = game.viewrange;
 
-               short[] tiles = new short[(r * 2 + 1) * (r * 2 + 1)];
+               int width = r * 2 + 1;
+               int height = r * 2 + 1;
+               short[] tiles = new short[width * height];
                int i = 0;
                //System.out.printf("map at %d,%d\n", x, y);
                for (int my = y - r; my <= y + r; my++) {
@@ -1662,41 +1689,43 @@ public class LivingThing extends DuskObject implements Runnable {
                        //System.out.println("\n");
                }
 
-               send(ServerMessage.mapMessage(x, y, tiles));
+               send(new MapMessage(MSG_UPDATE_MAP, width, height, x, y, tiles));
        }
 
-       public void chatMessage(String inMessage) {
-               if (inMessage == null) {
+       public void startBattle(LivingThing opponent) {
+               ListMessage lm = new ListMessage(MSG_BATTLE_UPDATE);
+               lm.add(FIELD_BATTLE_OPPONENT, opponent.name);
+               send(lm);
+       }
+
+       public void chatBattle(String msg) {
+               if (msg == null) {
                        return;
                }
                if (isPlayer()) {
-                       send(MessageType.Chat, inMessage + "\n");
+                       send(DuskMessage.create(MSG_BATTLE_CHAT, msg));
                }
                if (charmer != null) {
-                       charmer.chatMessage("From " + name + ": " + inMessage);
-                       return;
+                       charmer.chatBattle("From " + name + ": " + msg);
                }
        }
 
-       public void chatMessage(int red, int green, int blue, String inMessage) {
+       public void chatMessage(String inMessage) {
                if (inMessage == null) {
                        return;
                }
                if (isPlayer()) {
-                       if (!coloron) {
-                               chatMessage(inMessage);
-                               return;
-                       }
-                       String strResult = "" + (char) 23;
-                       strResult += red + "\n" + green + "\n" + blue + "\n" + inMessage + "\n";
-                       send(strResult);
+                       send(DuskMessage.create(MSG_CHAT, inMessage));
                }
                if (charmer != null) {
-                       charmer.chatMessage(red, green, blue, "From " + name + ": " + inMessage);
-                       return;
+                       charmer.chatMessage("From " + name + ": " + inMessage);
                }
        }
 
+       public void chatMessage(int red, int green, int blue, String inMessage) {
+               chatMessage(inMessage);
+       }
+
        public int getCharacterPoints() {
                int result = wisd
                                + inte
@@ -2173,27 +2202,142 @@ public class LivingThing extends DuskObject implements Runnable {
                        following = null;
                }
        }
+       final static int ASK_NEW_RACE = 0;
+
+       private DuskMessage getRaceQuery() {
+               ListMessage racem = new ListMessage(ASK_NEW_RACE);
+               // FIXME: confirm behaviour here
+               String dir = isPet() ? "defPets" : "defRaces";
+
+               racem.add(FIELD_QUERY_PROMPT, "Choose race");
+               racem.add(FIELD_QUERY_OPTIONS, Arrays.asList(new File(dir).list()));
+
+               return racem;
+       }
+
+       private DuskMessage getNewPlayerInfo() {
+               ListMessage np = new ListMessage(FIELD_AUTH_NEWPLAYER);
+               np.add(getRaceQuery());
+
+               return np;
+       }
+
+       static class TooManyFailures extends Exception {
+       }
 
        public void run() {
                connectionThread = Thread.currentThread();
+
+               String address = getAddress();
+
+               // Handle auth state.
                try {
+                       boolean haveuser = false;
+                       boolean create = false;
                        do {
-                               name = instream.readLine();
-                               if (name != null) {
-                                       name = name.trim();
+                               DuskMessage dm = readMessage();
+                               ListMessage res = new ListMessage(MSG_AUTH);
+
+                               // TODO: FIELD_AUTH_CLIENT stuff
+
+                               switch (dm.name) {
+                                       case MSG_AUTH: {
+                                               ListMessage lm = (ListMessage) dm;
+                                               String player = lm.getString(FIELD_AUTH_PLAYER, null);
+                                               String pass = lm.getString(FIELD_AUTH_PASS, null);
+                                               ListMessage newp = (ListMessage) lm.getMessage(FIELD_AUTH_NEWPLAYER);
+
+                                               // Don't give detailed reasons for failure (e.g. 'player not found') for security
+
+                                               if (player == null || pass == null) {
+                                                       // Just sent new player info
+                                                       res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_INCOMPLETE);
+                                                       res.add(FIELD_AUTH_REASON, "Login failed.");
+                                                       res.add(getNewPlayerInfo());
+                                               } else if (!game.isGoodName(player)) {
+                                                       // disallowed name
+                                                       res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_FAILED);
+                                                       res.add(FIELD_AUTH_REASON, "Name is not allowed, try again.");
+                                                       res.add(getNewPlayerInfo());
+                                               } else if (newp == null && playerExists(player)) {
+                                                       // Try normal login
+                                                       if (passwordCorrect(player, pass)) {
+                                                               haveuser = true;
+                                                       } else {
+                                                               res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_FAILED);
+                                                               res.add(FIELD_AUTH_REASON, "Login failed.");
+                                                       }
+                                               } else {
+                                                       System.out.println("? new player " + player);
+                                                       // Trying to create a player
+                                                       if (canCreate(player, newp, res)) {
+                                                               System.out.println("  creating player\n");
+                                                               haveuser = true;
+                                                               create = true;
+                                                       } else {
+                                                               if (res.get(FIELD_AUTH_NEWPLAYER) != null) {
+                                                                       res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_INCOMPLETE);
+                                                                       res.add(FIELD_AUTH_REASON, "Insufficient information to create player.");
+                                                               } else {
+                                                                       res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_EXISTS);
+                                                                       res.add(FIELD_AUTH_REASON, "A player with that name already exists.");
+                                                               }
+                                                       }
+                                               }
+
+                                               if (haveuser) {
+                                                       this.name = player;
+                                                       // security: don't store this?
+                                                       this.password = pass;
+
+                                                       game.passwordSuccess(player, address);
+                                                       game.registerPlayer(this, player);
+                                                       res.add(ID, FIELD_AUTH_RESULT, AUTH_LOGIN_OK);
+                                                       res.add(FIELD_AUTH_REASON, "Login ok.");
+                                               }
+                                               break;
+                                       }
+                                       default: // Anything else is a protocol error
+                                               res.add(-1L, FIELD_AUTH_RESULT, AUTH_LOGIN_FAILED);
+                                               res.add(FIELD_AUTH_REASON, "Unexpected message.");
+                                               break;
                                }
-                       } while (!getPlayer());
-                       if (!isWorking) {
-                               return;
-                       }
-               } catch (Exception e) {
-                       game.log.printError("LivingThing.run():start", e);
+
+                               send(res);
+                       } while (!haveuser);
+
+                       greetUser();
+                       loadPlayer(create);
+               } catch (BlockedIPException ex) {
+                       chatMessage("There's already a player connected from your IP address.");
+                       //Thread.sleep(1000);
+                       quit();
+                       closeNosavePlayer();
+                       return;
+               } catch (IOException ex) {
+                       game.log.printMessage(Log.INFO, getAddress() + ": login/create " + name + " " + ex);
+                       closeNosavePlayer();
+                       return;
+               } catch (ClassCastException ex) {
+                       game.log.printMessage(Log.INFO, getAddress() + ": code error " + name + " " + ex);
+                       // some server error
+                       quit();
                        closeNosavePlayer();
                        return;
+               } catch (TooManyFailures ex) {
+                       chatMessage("Too many login failures, try again in an hour.");
+                       quit();
+                       closeNosavePlayer();
+                       return;
+               } catch (Exception ex) {
+                       game.log.printMessage(Log.INFO, getAddress() + ": unexpected error " + name + " " + ex);
                }
+
+               // Player loaded, ready to go, send off all the shit
+
                connectionThread.setName("LivingThing(" + name + ")");
                sendThread.setName("LivingThing(" + name + ").send");
-               resizeMap();
+               initMap();
                changeLocBypass(x, y);
                updateInfo();
                updateStats();
@@ -2211,24 +2355,25 @@ public class LivingThing extends DuskObject implements Runnable {
                        chatMessage("You have entered the world hidden from players.");
                        game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " has entered the world hidden from players");
                }
-               String strInput,
-                               strStore;
                isSaveable = true;
                isReady = true;
-               while (true) {
-                       if (isStopped) {
-                               return;
-                       }
+               while (!isStopped) {
                        try {
-                               if (!isHalted) {
-                                       strInput = instream.readLine();
-                                       if (strInput != null) {
-                                               strInput = strInput.trim();
-                                       }
-                                       strStore = Commands.parseCommand(this, game, strInput);
-                                       if (strStore != null) {
-                                               chatMessage(strStore);
+                               DuskMessage dm = readMessage();
+
+                               switch (dm.name) {
+                                       case MSG_COMMAND: {
+                                               StringMessage sm = (StringMessage) dm;
+                                               String res = Commands.parseCommand(this, game, sm.value);
+
+                                               if (res != null)
+                                                       chatMessage(res);
+                                               break;
                                        }
+                                       default:
+                                               // anything else is bogus
+                                               System.out.println("Unexpected server command (ignored):");
+                                               dm.format(System.out);
                                }
                        } catch (Exception e) {
                                game.log.printError("LivingThing.run():" + name + " disconnected", e);
@@ -2238,272 +2383,146 @@ public class LivingThing extends DuskObject implements Runnable {
                }
        }
 
-       boolean getPlayer() {
-               if (!game.isGoodName(name)) {
-                       chatMessage("Not a valid name. This may because you left it blank, used invalid symbols, or made it too long. Please try again.");
-                       return false;
-               }
-               String strStore;
-               int i;
-               LivingThing thnStore;
-               try {
-                       File filStore = new File("users/" + name.toLowerCase());
-                       if (!filStore.exists() || filStore.length() < 100) {
-                               File filBackup = new File("users/" + name.toLowerCase() + ".backup");
-                               if (filBackup.exists()) {
-                                       filBackup.renameTo(filStore);
-                               }
-                       }
-
-                       String strIP = socket.getInetAddress().toString();
-                       int ip = strIP.indexOf("/");
-                       strIP = strIP.substring(ip + 1, strIP.length());
-
-                       if (filStore.exists()) {
-                               try (RandomAccessFile rafFile = new RandomAccessFile("users/" + name.toLowerCase(), "r")) {
-                                       chatMessage("enter your password:");
-                                       password = instream.readLine();
-                                       if (!password.equals(rafFile.readLine())) {
-                                               rafFile.close();
-                                               game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " entered the wrong password");
-                                               Variable failCount = game.varIP.getVariable(strIP);
-                                               if (failCount != null) {
-                                                       int fc = failCount.intValue();
-                                                       if (fc >= 4) {
-                                                               game.banAddress(strIP);
-                                                               chatMessage("Too many login failures, try again in an hour.");
-                                                               send("Goodbye.\n" + (char) 0);
-                                                               Thread.sleep(1000);
-                                                               closeNosavePlayer();
-                                                               rafFile.close();
-                                                               return true;
-                                                       } else {
-                                                               game.varIP.addVariable(strIP, fc + 1);
-                                                       }
-                                               } else {
-                                                       game.varIP.addVariable(strIP, (double) 1);
-                                               }
-                                               Thread.sleep(3000);
-                                               chatMessage("Incorrect Password.");
-                                               chatMessage("Enter your login name: ");
-                                               return false;
-                                       }
-                               } catch (Exception e) {
-                                       game.log.printError("getPlayer():" + socket.getInetAddress().toString() + ":" + name, e);
-                                       closeNosavePlayer();
-                                       return true;
-                               }
+       boolean playerExists(String name) {
+               File playerFile = new File("users/" + name.toLowerCase());
+
+               return playerFile.exists();
+       }
+
+       boolean passwordCorrect(String name, String pass) throws TooManyFailures, IOException, InterruptedException {
+
+               try (RandomAccessFile pf = new RandomAccessFile("users/" + name.toLowerCase(), "r")) {
+                       String userPass = pf.readLine();
+
+                       if (pass.equals(userPass)) {
+                               return true;
+                       } else if (game.passwordFailure(name, getAddress())) {
+                               throw new TooManyFailures();
                        } else {
-                               File fileStore = new File("pets/" + name.toLowerCase());
-                               if (fileStore.exists()) {
-                                       chatMessage(name + " is already in use.  Please choose another.");
-                                       return false;
-                               }
-                               chatMessage(name + ", Is that correct? (yes/no)");
-                               if (!instream.readLine().equalsIgnoreCase("yes")) {
-                                       chatMessage("Then what IS your name?");
-                                       return false;
-                               }
-                               chatMessage("Enter a new password:");
-                               password = instream.readLine();
-                               chatMessage("Confirm that password:");
-                               while (!password.equals(instream.readLine())) {
-                                       chatMessage("Passwords did not match, enter a new password:");
-                                       password = instream.readLine();
-                                       chatMessage("Confirm that password:");
-                               }
-                       }
-                       game.varIP.removeVariable(strIP);
-                       try {
-                               wornItems = new Equipment();
-                               itemList = new ItemList();
-                               conditions.clear();
-                               nearEntities.clear();
-                               flags.clear();
-                               ignoreList.clear();
-                               chatMessage("Login Accepted.");
-                               proceed();
-                               chatMessage("This game is running DuskServer v" + game.version + ". http://dusk.wesowin.org/");
-                               chatMessage("Started at " + game.datStart.toString() + ".");
-                               for (LivingThing lt : game.playersByName.values()) {
-                                       if (name.equalsIgnoreCase(lt.name)) {
-                                               game.log.printMessage(Log.INFO, socket.getInetAddress().toString() + ":" + name + " tried to log in twice");
-                                               chatMessage("That user is already logged in. They are being logged out.");
-                                               lt.chatMessage("There has been another logon under this name, you are being logged out.");
-                                               lt.close();
-                                               break;
-                                       }
-                                       // Second IP Filter check to catch delayed sign-ons
-                                       if (game.blnIPF) {
-                                               String IP = lt.socket.getInetAddress().toString();
-                                               if (IP.equalsIgnoreCase(socket.getInetAddress().toString())) {
-                                                       chatMessage("There's already a player connected from your IP address.");
-                                                       Thread.sleep(1000);
-                                                       closeNosavePlayer();
-                                                       return false;
-                                               }
-                                       }
-                               }
-                               game.playersByName.put(this.name.toLowerCase(), this);
-                               game.onStart(this);
-                               try (RandomAccessFile rafFile = new RandomAccessFile("users/default", "r")) {
-                                       strStore = rafFile.readLine();
-                                       while (!(strStore == null || strStore.equals("."))) {
-                                               parseUserFile(rafFile, strStore);
-                                               strStore = rafFile.readLine();
-                                       }
-                               }
-                       } catch (Exception e) {
-                               game.log.printError("getPlayer():While loading default user file for " + name, e);
-                       }
-                       strStore = name.toLowerCase();
-                       File filPlayer = new File("users/" + strStore);
-                       File filBackup = new File("users/" + strStore + ".backup");
-                       File filCheck;
-                       int i2 = 0;
-                       if (filBackup.exists()) {
-                               if (filPlayer.length() > filBackup.length()) {
-                                       filCheck = new File("backup/" + strStore + ".possiblyDamaged");
-                                       while (filCheck.exists()) {
-                                               i2++;
-                                               filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i2);
-                                       }
-                                       filBackup.renameTo(filCheck);
-                               } else if (filPlayer.length() < filBackup.length()) {
-                                       filCheck = new File("backup/" + strStore + ".possiblyDamaged");
-                                       while (filCheck.exists()) {
-                                               i2++;
-                                               filCheck = new File("backup/" + strStore + ".possiblyDamaged." + i2);
-                                       }
-                                       filPlayer.renameTo(filCheck);
-                                       filBackup.renameTo(new File("users/" + strStore));
-                               }
-                       }
-                       /*
-                        ** Load the user
-                        */
-                       file = new File("users/", strStore);
-                       backup = new File("users/", strStore + ".backup");
-                       try (RandomAccessFile rafFile = new RandomAccessFile(file, "rw")) {
-                               strStore = rafFile.readLine();
-                               while (!(strStore == null || strStore.equals("."))) {
-                                       parseUserFile(rafFile, strStore);
-                                       strStore = rafFile.readLine();
-                               }
+                               Thread.sleep(3000);
+                               return false;
                        }
-                       /*
-                        ** Try to load a pet, if they don't already have one
-                        */
-                       if (following == null) {
-                               following = new LivingThing("default", null, this, game);
-                               if (following.name.equalsIgnoreCase("default")) {
-                                       following.closeNosavePlayer();
-                                       following = null;
-                               }
+               }
+       }
+
+       /**
+        * Check if the user can be created, unique name, and all requirements met
+        *
+        * @param name
+        * @param np NEWPLAYER message containing new player infos.
+        * @param res a NEWPLAYER message populated iwth missing bits
+        * @return true if ok
+        */
+       boolean canCreate(String name, ListMessage np, ListMessage res) {
+               boolean ok = true;
+               ListMessage newp = null;
+
+               // Check if we have the info we need to create the user - i.e. race so far
+               if (np == null
+                               || (race = np.getString(ASK_NEW_RACE, race)) == null
+                               || !checkRace(race)) {
+                       newp = new ListMessage(FIELD_AUTH_NEWPLAYER);
+                       newp.add(getRaceQuery());
+                       res.add(newp);
+                       ok = false;
+               }
+
+               ok &= !new File("users/" + name.toLowerCase()).exists();
+               ok &= !new File("pets/" + name.toLowerCase()).exists();
+
+               return ok;
+       }
+
+       void greetUser() {
+               chatMessage("DuskZ Server " + game.version + " --  http://code.google.com/p/duskz/");
+               chatMessage("Started at " + game.datStart.toString() + ".");
+               chatMessage("You're playing DuskZ, a graphical mud.");
+       }
+
+       void loadPlayer(boolean create) throws IOException {
+               wornItems = new Equipment();
+               itemList = new ItemList();
+               conditions.clear();
+               nearEntities.clear();
+               flags.clear();
+               ignoreList.clear();
+
+               game.onStart(this);
+
+               /**
+                * Load default user - ignore errors
+                */
+               try {
+                       loadUserFile(new File("users/default"), false);
+               } catch (IOException e) {
+                       game.log.printError("getPlayer():While loading default user file for " + name, e);
+               }
+
+               file = new File("users", name.toLowerCase());
+               backup = new File("users", name.toLowerCase() + ".backup");
+
+               /**
+                * Load actual user
+                */
+               if (!create)
+                       loadUserFile(file, true);
+
+               /*
+                ** Try to load a pet, if they don't already have one
+                */
+               if (following == null) {
+                       following = new LivingThing("default", null, this, game);
+                       if (following.name.equalsIgnoreCase("default")) {
+                               following.closeNosavePlayer();
+                               following = null;
                        }
-               } catch (Exception e) {
-                       game.log.printError("getPlayer():While loading file for " + name, e);
-                       closeNosavePlayer();
-                       return true;
                }
+               //      } catch (Exception e) {
+               //              game.log.printError("getPlayer():While loading file for " + name, e);
+               //              closeNosavePlayer();
+               //              return true;
+               //      }
                loadRace();
                isLoaded = true;
                if (following != null) {
                        following.isLoaded = true;
                        following.changeLocBypass(x, y);
                }
-               return true;
-       }
 
-       String askRace(String strRaceDir, String prompt) throws IOException {
-               File filRaces = new File(strRaceDir);
-               String strList[] = filRaces.list();
-               StringBuilder sb = new StringBuilder(prompt);
-               sb.append("\n");
-               for (String s : strList) {
-                       sb.append(s).append("\n");
+               if (create) {
+                       isSaveNeeded = true;
+                       savePlayer();
                }
-               sb.append(".\n");
-               String s = sb.toString();
+       }
 
-               // ??
-               if (isPlayer()) {
-                       send(MessageType.ChooseRace, s);
-               }
-               if (charmer != null) {
-                       charmer.send(MessageType.ChooseRace, s);
-               }
-               if (isPet()) {
-                       return master.instream.readLine().toLowerCase();
-               } else {
-                       return instream.readLine().toLowerCase();
-               }
+       boolean checkRace(String race) {
+               // FIXME: confirm behaviour here
+               String dir = isPet() ? "defPets" : "defRaces";
+
+               return new File(dir, race).exists();
        }
 
        public void loadRace() {
-               String s;
-               String dir;
-               if (isPet()) {
-                       dir = "defPets";
-               } else {
-                       dir = "defRaces";
-               }
+               String dir = isPet() ? "defPets" : "defRaces";
+
                try {
-                       if (race == null || !(new File(dir + "/" + race).exists())) {
-                               s = askRace(dir, "Choose one of the following races:");
-                               // FIXME: check this isn't broken
-                               File filCheck = new File(dir + "/" + s);
-                               while (s.equals("") || !filCheck.exists()) {
-                                       s = askRace(dir, "That is not a valid race, please choose again.");
-                                       filCheck = new File(dir + "/" + s);
-                               }
-                               race = s;
-                       }
-                       loadRaceFile(new File(dir + "/" + race), true);
+                       loadRaceFile(new File(dir, race), true);
                } catch (Exception e) {
                        game.log.printError("loadRace():Loading " + name + "'s race file \"" + dir + "/" + race + "\"", e);
                }
        }
 
        public void unloadRace() {
-               String dir;
-               if (isPet()) {
-                       dir = "defPets";
-               } else {
-                       dir = "defRaces";
-               }
+               String dir = isPet() ? "defPets" : "defRaces";
+
                try {
-                       if (race == null || !(new File(dir + "/" + race).exists())) {
-                               race = null;
-                               return;
-                       }
-                       loadRaceFile(new File(dir + "/" + race), false);
+                       loadRaceFile(new File(dir, race), false);
                } catch (Exception e) {
                        game.log.printError("unloadRace():Un-loading " + name + "'s race file \"" + dir + "/" + race + "\"", e);
                }
                race = null;
        }
 
-       public void updateAppletImages() {
-               String strResult = "" + (char) 1;
-               strResult += game.strRCAddress + "\n";
-               try {
-                       send(strResult);
-               } catch (Exception e) {
-                       game.log.printError("updateAppletImages()", e);
-               }
-       }
-
-       public void updateApplicationImages() {
-               String strResult = "" + (char) 1;
-               strResult += game.strRCName + "\n";
-               try {
-                       send(strResult);
-               } catch (Exception e) {
-                       game.log.printError("updateApplicationImages()", e);
-               }
-       }
-
        public void updateMusic() {
                /*try
                 {
@@ -2565,39 +2584,34 @@ public class LivingThing extends DuskObject implements Runnable {
 
        public void playSFX(int intSFX) {
                if (audioon) {
-                       try {
-                               send((char) 15 + "" + intSFX + "\n");
-                       } catch (Exception e) {
-                               game.log.printError("playSFX()", e);
-                       }
+                       //      try {
+                       //              send((char) 15 + "" + intSFX + "\n");
+                       //      } catch (Exception e) {
+                       //              game.log.printError("playSFX()", e);
+                       //      }
                }
        }
 
        public void updateActions() {
-               try {
-                       if (isPlayer()) {
-                               String strResult = "" + (char) 10;
-                               if (battle != null) {
-                                       strResult += "flee\n";
+               DuskMessage.StringListMessage list = new DuskMessage.StringListMessage(MSG_UPDATE_ACTIONS);
+
+               if (isPlayer()) {
+                       if (battle != null) {
+                               list.add("flee");
+                       } else {
+                               if (isSleeping) {
+                                       list.add("wake");
                                } else {
-                                       if (isSleeping) {
-                                               strResult += "wake\n";
-                                       } else {
-                                               strResult += "sleep\n";
-                                       }
+                                       list.add("sleep");
                                }
-                               strResult += ".\n";
-                               send(strResult);
                        }
-               } catch (Exception e) {
-                       game.log.printError("updateActions()", e);
                }
+               send(list);
        }
 
-       // FIXME: I think this should be encapsulated in a more desriptive message
        public void updateEquipment() {
                try {
-                       send(MessageType.UpdateEquipment, wornItems.toEntity());
+                       send(wornItems.toMessage(MSG_EQUIPMENT));
                } catch (Exception e) {
                        game.log.printError("updateEquipment():" + name + " disconnected", e);
                        isStopped = true;
@@ -2605,24 +2619,165 @@ public class LivingThing extends DuskObject implements Runnable {
                }
        }
 
-       public void send(MessageType type, String data) {
+       public void quit() {
+               send(DuskMessage.create(MSG_QUIT));
+       }
+
+       public void viewText(String name, boolean editable, String text) {
+               // FIXME: some error
+               if (privs <= 2)
+                       return;
+
+               ListMessage lm = new ListMessage(MSG_VIEW_TEXT);
+               lm.add(FIELD_TEXT_NAME, name);
+               lm.add(FIELD_TEXT_EDITABLE, (byte) (editable ? 1 : 0));
+               if (text != null)
+                       lm.add(FIELD_TEXT_TEXT, text);
+
+               send(lm);
+       }
+
+       /**
+        * This should only be called from login stuff,
+        * and then the main input loop.
+        *
+        * TODO: put this and the instream stuff into a separate object
+        *
+        * @return
+        * @throws IOException
+        */
+       private DuskMessage readMessage() throws IOException {
+               DuskMessage msg;
+
+               do {
+                       synchronized (instream) {
+                               msg = DuskMessage.receiveMessage(instream);
+                       }
+
+                       // Redirect queries to whomever is waiting for it
+                       if (msg.name == MSG_QUERY) {
+                               EntityListMessage qmsg = (EntityListMessage) msg;
+                               PendingQuery f;
+
+                               synchronized (pendingQuestions) {
+                                       f = pendingQuestions.get(qmsg.id);
+                               }
+
+                               f.setResponse(qmsg);
+                       }
+               } while (msg.name == MSG_QUERY);
+
+               return msg;
+       }
+
+       public void send(int msg, String data) {
                if (isPlayer() && isWorking && !isClosing) {
-                       // FIXME: put code in senddata
-                       messageQueue.offer(ServerMessage.stringMessage(type, data));
+                       messageQueue.offer(DuskMessage.create(msg, data));
                }
        }
 
-       public void send(ServerMessage msg) {
+       public void sendUrgent(DuskMessage msg) {
                if (isPlayer() && isWorking && !isClosing) {
-                       messageQueue.offer(msg);
+                       messageQueue.addFirst(msg);
                }
        }
 
-       public void send(String data) {
+       public void send(DuskMessage msg) {
                if (isPlayer() && isWorking && !isClosing) {
-                       messageQueue.offer(ServerMessage.stringMessage(data));
+                       messageQueue.offer(msg);
                }
        }
+       long qid;
+       final HashMap<Long, PendingQuery> pendingQuestions = new HashMap<>();
+
+       synchronized long getQuestionID() {
+               return qid++;
+       }
+
+       static class PendingQuery implements Future<EntityListMessage> {
+
+               EntityListMessage query;
+               EntityListMessage response;
+
+               public PendingQuery(EntityListMessage query) {
+                       this.query = query;
+               }
+
+               synchronized void setResponse(EntityListMessage response) {
+                       this.response = response;
+                       notify();
+               }
+
+               @Override
+               public boolean cancel(boolean mayInterruptIfRunning) {
+                       throw new UnsupportedOperationException("Not supported yet.");
+               }
+
+               @Override
+               public boolean isCancelled() {
+                       throw new UnsupportedOperationException("Not supported yet.");
+               }
+
+               @Override
+               public boolean isDone() {
+                       throw new UnsupportedOperationException("Not supported yet.");
+               }
+
+               @Override
+               public synchronized EntityListMessage get() throws InterruptedException, ExecutionException {
+                       while (response == null) {
+                               wait();
+                       }
+                       return response;
+               }
+
+               @Override
+               public EntityListMessage get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+                       if (response == null) {
+                               wait(unit.convert(timeout, unit));
+                               if (response == null)
+                                       throw new TimeoutException();
+                       }
+                       return response;
+               }
+       }
+
+       /**
+        * This asks a query asynchronously
+        * FIXME: abnormal shutdown requires the queries to be flushed
+        *
+        * @param query
+        * @return a future used to retrieve the query
+        */
+       public Future<EntityListMessage> askQuestion(EntityListMessage query) throws IOException {
+               PendingQuery pq = new PendingQuery(query);
+
+               if (query.name != MSG_QUERY)
+                       throw new RuntimeException("Trying to ask non-query message");
+
+               synchronized (pendingQuestions) {
+                       query.id = qid++;
+                       pendingQuestions.put(query.id, pq);
+               }
+
+               messageQueue.add(query);
+
+               // Just discard messages until we get a response
+               // And what if we don't?
+               if (Thread.currentThread() == connectionThread) {
+                       do {
+                               readMessage();
+                       } while (pq.response == null);
+               }
+
+               return pq;
+       }
+
+       //public void send(String data) {
+       //      if (isPlayer() && isWorking && !isClosing) {
+       //              messageQueue.offer(DuskMessage.create(MSG_CHAT, data));
+       //      }
+       //}
 
        /*      public void send(byte data)
         {
@@ -2642,248 +2797,122 @@ public class LivingThing extends DuskObject implements Runnable {
         }
         }*/
        public void updateInfo() {
-               try {
-                       String strResult;
-                       strResult = hp + "\n";
-                       strResult += (maxhp + hpbon) + "\n";
-                       strResult += mp + "\n";
-                       strResult += (maxmp + mpbon) + "\n";
-                       send(MessageType.UpdateStats, strResult);
-               } catch (Exception e) {
-                       game.log.printError("updateInfo():" + name + " disconnected", e);
-                       isStopped = true;
-               }
+               ListMessage lm = new ListMessage(MSG_INFO_PLAYER);
+
+               lm.add(FIELD_INFO_HP, hp);
+               lm.add(FIELD_INFO_MAXHP, maxhp + hpbon);
+               lm.add(FIELD_INFO_MP, hp);
+               lm.add(FIELD_INFO_MAXMP, maxmp + mpbon);
+
+               send(lm);
        }
 
        public void updateRange() {
-               try {
-                       String strResult = "" + (char) 28;
-                       strResult += getRangeWithBonus() + "\n";
-                       send(strResult);
-               } catch (Exception e) {
-                       game.log.printError("updateRange():" + name + " disconnected", e);
-               }
+               ListMessage lm = new ListMessage(MSG_INFO_PLAYER);
+
+               lm.add(FIELD_INFO_RANGE, getRangeWithBonus());
+
+               send(lm);
        }
 
+       // Use updateInventory instead
+       @Deprecated
        public void updateItems() {
-               try {
-                       StringBuilder sb = new StringBuilder();
-
-                       for (LinkedList<Item> qStore : itemList.values()) {
-                               if (qStore.size() > 0) {
-                                       Item itmStore = (Item) qStore.element();
-                                       if (itmStore.isArmor()) {
-                                               sb.append((2 + itmStore.intKind) + "\n");
-                                       } else if (itmStore.isWeapon()) {
-                                               sb.append("1\n");
-                                       } else {
-                                               sb.append("0\n");
-                                       }
-                                       sb.append(itmStore.name + "\n");
-                               }
-                       }
-                       sb.append(".\n");
-                       send(MessageType.UpdateItems, sb.toString());
-                       if (game.overMerchant(x, y) != null) {
-                               updateSell();
-                       }
-                       if (game.overPlayerMerchant(x, y) != null) {
-                               updateSell();
-                       }
-               } catch (Exception e) {
-                       game.log.printError("updateItems():" + name + " disconnected", e);
-                       isStopped = true;
-               }
+               updateInventory();
+
+               // FIXME: change on observe?  This aint no quantum effect!
                isSaveNeeded = true;
        }
 
-       public void updateStats() {
-               SpellGroup grpStore;
-               int i,
-                               i2;
+       private void addStat(ListMessage lm, int field, int val, int valbon) {
+               lm.add(field, val);
+               if (valbon != 0)
+                       lm.add(field + 1, valbon);
+       }
 
-               // FIXME: wtf, duplicated shit code again
-               // FIXME: convert to stringbuilder
-               try {
-                       String strResult = "";
-                       strResult += cash + " gp\n";
-                       strResult += exp + " exp\n";
-                       if (strebon == 0) {
-                               strResult += "str: " + stre + "\n";
-                       } else {
-                               strResult += "str: " + stre + " + " + strebon + "\n";
-                       }
-                       if (intebon == 0) {
-                               strResult += "int: " + inte + "\n";
-                       } else {
-                               strResult += "int: " + inte + " + " + intebon + "\n";
-                       }
-                       if (dextbon == 0) {
-                               strResult += "dex: " + dext + "\n";
-                       } else {
-                               strResult += "dex: " + dext + " + " + dextbon + "\n";
-                       }
-                       if (consbon == 0) {
-                               strResult += "con: " + cons + "\n";
-                       } else {
-                               strResult += "con: " + cons + " + " + consbon + "\n";
-                       }
-                       if (wisdbon == 0) {
-                               strResult += "wis: " + wisd + "\n";
-                       } else {
-                               strResult += "wis: " + wisd + " + " + wisdbon + "\n";
-                       }
-                       if (dammodbon == 0) {
-                               strResult += "DamMod: " + getDamMod() + "\n";
-                       } else {
-                               strResult += "DamMod: " + getDamMod() + " + " + dammodbon + "\n";
-                       }
-                       if (acbon == 0) {
-                               strResult += "AC: " + getArmorMod() + "\n\n";
-                       } else {
-                               strResult += "AC: " + getArmorMod() + " + " + acbon + "\n";
-                       }
-                       strResult += "-Affected by-\n";
-                       for (Condition cond : conditions) {
-                               if (cond.display) {
-                                       strResult += cond.name + "\n";
-                               }
-                       }
-                       strResult += "-Skills-\n";
-                       for (Ability skill : skillMap.values()) {
-                               strResult += skill.name + ": " + skill.getAbility() + "\n";
-                       }
-                       strResult += "-Spells-\n";
-                       for (Ability spell : spellMap.values()) {
-                               grpStore = game.getSpellGroup(spell.name);
-                               if (grpStore != null) {
-                                       strResult += spell.name + ": " + spell.getAbility() + "\n";
-                                       strResult += grpStore.spellList(spell.getAbility());
-                               }
-                       }
-                       if (master != null) {
-                               strResult += "\nFollowing: " + master.name + "\n";
-                       }
-                       if (following != null) {
-                               strResult += "\nFollowed By: " + following.name + "\n";
-                               if (following.isPet()) {
-                                       strResult += following.hp + "/" + following.maxhp + " hp\n";
-                                       strResult += following.mp + "/" + following.maxmp + " mp\n";
-                                       strResult += following.cash + " gp\n";
-                                       strResult += following.exp + " exp\n";
-                                       if (following.strebon == 0) {
-                                               strResult += "str: " + following.stre + "\n";
-                                       } else {
-                                               strResult += "str: " + following.stre + " + " + following.strebon + "\n";
-                                       }
-                                       if (following.intebon == 0) {
-                                               strResult += "int: " + following.inte + "\n";
-                                       } else {
-                                               strResult += "int: " + following.inte + " + " + following.intebon + "\n";
-                                       }
-                                       if (following.dextbon == 0) {
-                                               strResult += "dex: " + following.dext + "\n";
-                                       } else {
-                                               strResult += "dex: " + following.dext + " + " + following.dextbon + "\n";
-                                       }
-                                       if (following.consbon == 0) {
-                                               strResult += "con: " + following.cons + "\n";
-                                       } else {
-                                               strResult += "con: " + following.cons + " + " + following.consbon + "\n";
-                                       }
-                                       if (following.wisdbon == 0) {
-                                               strResult += "wis: " + following.wisd + "\n";
-                                       } else {
-                                               strResult += "wis: " + following.wisd + " + " + following.wisdbon + "\n";
-                                       }
-                                       if (following.dammodbon == 0) {
-                                               strResult += "DamMod: " + following.getDamMod() + "\n";
-                                       } else {
-                                               strResult += "DamMod: " + following.getDamMod() + " + " + following.dammodbon + "\n";
-                                       }
-                                       if (following.acbon == 0) {
-                                               strResult += "AC: " + following.getArmorMod() + "\n\n";
-                                       } else {
-                                               strResult += "AC: " + following.getArmorMod() + " + " + following.acbon + "\n";
-                                       }
-                                       strResult += "-Affected by-\n";
-                                       for (Condition cndStore : following.conditions) {
-                                               if (cndStore.display) {
-                                                       strResult += cndStore.name + "\n";
-                                               }
-                                       }
-                                       strResult += "-Skills-\n";
-                                       for (Ability skill : following.skillMap.values()) {
-                                               strResult += skill.name + ": " + skill.getAbility() + "\n";
-                                       }
-                                       strResult += "-Spells-\n";
-                                       for (Ability spell : following.skillMap.values()) {
-                                               grpStore = game.getSpellGroup(spell.name);
-                                               if (grpStore != null) {
-                                                       strResult += spell.name + ": " + spell.getAbility() + "\n";
-                                                       strResult += grpStore.spellList(spell.getAbility());
-                                               }
-                                       }
-                               }
+       private ListMessage buildStats(LivingThing lt, int msg) {
+               ListMessage lm = new ListMessage(msg);
+
+               lm.add(FIELD_INFO_CASH, cash);
+               lm.add(FIELD_INFO_EXP, exp);
+               addStat(lm, FIELD_INFO_STR, stre, strebon);
+               addStat(lm, FIELD_INFO_INT, inte, intebon);
+               addStat(lm, FIELD_INFO_DEX, dext, dextbon);
+               addStat(lm, FIELD_INFO_CON, cons, consbon);
+               addStat(lm, FIELD_INFO_WIS, wisd, wisdbon);
+               addStat(lm, FIELD_INFO_DAM, getDamMod(), dammodbon);
+               addStat(lm, FIELD_INFO_ARC, getArmorMod(), acbon);
+
+               DuskMessage.StringListMessage list;
+
+               lm.add(list = new DuskMessage.StringListMessage(FIELD_INFO_CONDITIONS));
+               for (Condition cond : conditions) {
+                       if (cond.display) {
+                               list.add(cond.name);
                        }
-                       strResult += ".\n";
-                       send(MessageType.UpdateInfo, strResult);
-               } catch (Exception e) {
-                       game.log.printError("updateStats():" + name + " disconnected", e);
-                       isStopped = true;
                }
-               //updateRange();
-               isSaveNeeded = true;
-       }
 
-       public void halt() {
-               isHalted = true;
-               try {
-                       send(MessageType.Halt, "");
-               } catch (Exception e) {
-                       isHalted = false;
-                       game.log.printError("halt()", e);
+               lm.add(list = new DuskMessage.StringListMessage(FIELD_INFO_SKILLS));
+               for (Ability skill : skillMap.values()) {
+                       list.add(skill.name + ": " + skill.getAbility());
+               }
+
+               lm.add(list = new DuskMessage.StringListMessage(FIELD_INFO_SPELLS));
+               for (Ability spell : spellMap.values()) {
+                       SpellGroup sg = game.getSpellGroup(spell.name);
+                       if (sg != null) {
+                               list.add(spell.name + ": " + spell.getAbility());
+                               sg.spellList(list.value, spell.getAbility());
+                       }
+               }
+               if (!lt.isPet()) {
+                       if (master != null)
+                               lm.add(FIELD_INFO_FOLLOWING, master.name);
+                       if (following != null)
+                               lm.add(FIELD_INFO_FOLLOWED, following.name);
                }
+               return lm;
        }
 
-       public void proceed() {
-               isHalted = false;
-               try {
-                       send(MessageType.Proceed, ID + "\n");
-               } catch (Exception e) {
-                       game.log.printError("proceed()", e);
+       public void updateStats() {
+               send(buildStats(this, MSG_INFO_PLAYER));
+               if (following != null && following.isPet()) {
+
+                       send(buildStats(this, MSG_INFO_PET));
                }
+
+               isSaveNeeded = true;
        }
 
        public void stillThere() {
-               try {
-                       send("" + (char) 13);
-               } catch (Exception e) {
-                       game.log.printError("stillThere()", e);
-               }
+               send(DuskMessage.create(MSG_PING));
        }
 
-       public void resizeMap() {
-               int i, i2;
-               String strResult = (char) 19 + "";
-               strResult += game.mapsize + "\n";
-               send(strResult);
+       public void initMap() {
+               ListMessage lm = new ListMessage(MSG_INIT_MAP);
+
+               lm.add(FIELD_MAP_WIDTH, game.mapsize);
+               lm.add(FIELD_MAP_HEIGHT, game.mapsize);
+               // FIXME: depends on client info
+               lm.add(FIELD_MAP_ASSETLOCATION, game.strRCName);
+
+               send(lm);
        }
 
-       public void updateSell() {
-               StringBuilder sb = new StringBuilder();
+       public void updateInventory() {
+               TransactionMessage tm = new TransactionMessage(MSG_INVENTORY);
 
                for (LinkedList<Item> list : itemList.values()) {
-                       for (Item item : list) {
-                               sb.append(item.intCost / 2).append("gp)").append(item.name).append("\n");
+                       if (!list.isEmpty()) {
+                               Item item = list.get(0);
+                               tm.add(item.getWearLocation(), item.name, list.size(), item.intCost / 2, "gp");
                        }
                }
-               sb.append(".\n");
-               send(MessageType.UpdateSell, sb.toString());
+               send(tm);
        }
 
        void offMerchant() {
-               send(MessageType.ExitMerchant, "");
+               send(DuskMessage.create(MSG_EXIT_MERCHANT));
        }
 
        private class SendThread extends Thread {
@@ -2892,12 +2921,17 @@ public class LivingThing extends DuskObject implements Runnable {
                }
 
                public void run() {
-                       ServerMessage msg;
+                       DuskMessage msg;
+
                        while (!isStopped) {
                                try {
                                        msg = messageQueue.take();
+
+                                       // low level protocol dump
+                                       msg.format(System.out);
+
                                        try {
-                                               msg.send(outstream);
+                                               msg.sendMessage(outstream);
                                                outstream.flush();
                                        } catch (IOException e) {
                                                game.log.printError("SendThread.run():" + msg + " to " + name, e);
index ca9764c..58aec25 100644 (file)
@@ -83,7 +83,14 @@ public class Merchant extends DuskObject {
                        if (thnMaster.isPet()) {
                                thnMaster.chatMessage("You ARE a pet!");
                                return;
+                       
                        }
+                       
+                       thnMaster.chatMessage("The developer hasn't implemented pets yet!, see Merchangt.java line 90");
+                       if (true)return;
+               
+                       // FIXME: requires query system.  Use callbacks?
+                       /*
                        thnMaster.halt();
                        thnMaster.chatMessage("Enter a name for your pet:");
                        String strName = thnMaster.instream.readLine().trim();
@@ -110,10 +117,11 @@ public class Merchant extends DuskObject {
                                filCheck = new File("defPets/" + strStore);
                        }
                        thnMaster.following = new LivingThing(strName, strStore, thnMaster, engGame);
+                       */
                } catch (Exception e) {
                        engGame.log.printError("Merchant.pet()", e);
                } finally {
-                       thnMaster.proceed();
+                       //thnMaster.proceed();
                }
        }
 
index b39a324..5055ac3 100644 (file)
@@ -21,7 +21,6 @@
  */
 package duskz.client;
 
-import java.io.DataInputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -197,49 +196,6 @@ public class ClientMap {
                                && ly >= 0 && ly < rows;
        }
 
-       public synchronized void updateTiles(int offx, int offy, DataInputStream instream) throws IOException {
-               //for (int x = 0; x < cols; x++) {
-               //      for (int y = 0; y < rows; y++) {
-               //              setTile(x, y, Short.parseShort(instream.readLine()));
-               //      }
-               //}
-               int length = instream.readInt();
-               if (length != cols * rows) {
-                       throw new IOException("Protocol error incorrect map size");
-               }
-               for (int y = 0; y < rows; y++) {
-                       for (int x = 0; x < cols; x++) {
-                               setTile(x, y, instream.readShort());
-                       }
-               }
-
-               this.offx = offx;
-               this.offy = offy;
-
-               // Prune out of range thingies
-               List<Entity> removing = new ArrayList<>();
-               for (Entry<Integer, List<Entity>> es : entityByLocation.entrySet()) {
-                       int ex = es.getKey() & 0xffff;
-                       int ey = (es.getKey() >> 16) & 0xffff;
-
-                       System.out.printf("check inside %d,%d = %s\n", ex, ey, inside(ex, ey));
-
-                       if (!inside(ex, ey)) {
-                               removing.addAll(es.getValue());
-                       }
-               }
-
-               // TODO: this could be optimised
-               // TODO: does the server need to know this
-               if (!removing.isEmpty()) {
-                       System.out.println("offset: " + this.offx + "," + this.offy);
-                       for (Entity e : removing) {
-                               System.out.println("removing out of range :" + e);
-                               removeEntity(e);
-                       }
-               }
-       }
-
        public synchronized void updateTiles(int offx, int offy, short[] tiles) throws IOException {
                //for (int x = 0; x < cols; x++) {
                //      for (int y = 0; y < rows; y++) {
index aedb57f..6008d55 100644 (file)
  * Feb-2013 Michael Zucchi - modernised java, cleanup, abstracted the GUI behind
  * an interface. Abstracted most commands. Added some binary protocol changes.
  * Track the player id for better battles.
+ * Mar-2013 Michael Zucchi - new protocol implementation.
  */
 package duskz.client;
 
-import duskz.protocol.MessageType;
-import duskz.protocol.ServerMessage;
-import duskz.protocol.ServerMessage.EntityMessage;
+import duskz.protocol.*;
+import duskz.protocol.DuskMessage.*;
+import static duskz.protocol.DuskProtocol.FIELD_AUTH_NEWPLAYER;
+import static duskz.protocol.DuskProtocol.FIELD_AUTH_REASON;
 import java.io.*;
 import java.net.*;
 import java.util.ArrayList;
@@ -48,10 +50,10 @@ import java.util.logging.Logger;
  *
  * @author notzed
  */
-public class Dusk implements Runnable {
+public class Dusk implements Runnable, DuskProtocol {
 
        public final static boolean dumpRaw = false;
-       static String strVersion = "2.7.1.W3";
+       static String strVersion = "3.0 dev";
        Status status = new Status();
        long loncash;
        boolean loaded,
@@ -67,19 +69,28 @@ public class Dusk implements Runnable {
        List<TransactionItem> buyList = new ArrayList<>();
        List<TransactionItem> sellList = new ArrayList<>();
        Equipment worn = new Equipment();
-       Socket socket;
-       DataOutputStream outstream;
-       DataInputStream instream;
+       private Socket socket;
+       private DataOutputStream outstream;
+       private DataInputStream instream;
        int mapSize = 11;
        ClientMap map;
        boolean intStep,
                        blnMusic = true;
-       Thread thrRun;
+       Thread gameThread;
        int tileSize = 32;
        int spriteSize = 64;
        GUI frame;
        String address = "dusk.wesowin.org";
        int port = 7423;
+       // TODO: management at both ends could be put into the protocol namespace
+       // outstanding queries, do one at a time
+       ListMessage pendingQueries;
+       int pendingQueryIndex;
+       int queryID;
+       // message to send when all queries complete
+       ListMessage queryMessage;
+       // Where to put the query fields
+       ListMessage queryFields;
 
        public Dusk(GUI gui) {
                frame = gui;
@@ -91,9 +102,15 @@ public class Dusk implements Runnable {
                        //frame.initComponents();
                        //frame.setVisible(true);
                        frame.logClear();
-                       frame.log("Dusk Client v" + strVersion + " -- http://FIXME/\n");
+                       frame.log("DuskZ Client " + strVersion + " -- http://code.google.com/p/duskz/\n");
                        frame.log("You are using Java version " + System.getProperty("java.version") + "\n");
 
+                       frame.log("Copyright (C) 2000 Tom Weingarten\n");
+                       frame.log("Copyright (C) 2013 Michael Zucchi\n");
+                       frame.log("This program comes with ABSOLUTELY NO WARRANTY.\n");
+                       frame.log("This is free software, and you are welcome to redistribute it\n");
+                       frame.log("under certain conditions.\n");
+
                        if (blnApplet) {
                                //address = appShell.getParameter("address");
                                //port = Integer.parseInt(appShell.getParameter("port"));
@@ -137,6 +154,7 @@ public class Dusk implements Runnable {
        }
        State state = State.Unconnected;
        String pass;
+       String user;
 
        public void connect(String address, int port, String user, String pass) {
                // Connect to Server
@@ -149,8 +167,8 @@ public class Dusk implements Runnable {
                        state = State.Connected;
 
                        // Load Images and start checking for incoming commands
-                       thrRun = new Thread(this);
-                       thrRun.start();
+                       gameThread = new Thread(this);
+                       gameThread.start();
                        connected = true;
                        //Initialize objects
                        map = new ClientMap(11, 11);
@@ -158,8 +176,15 @@ public class Dusk implements Runnable {
                        sellList.clear();
 
                        this.pass = pass;
+                       this.user = user;
                        state = State.Username;
-                       command(user);
+
+                       // try a login command
+                       ListMessage lm = new ListMessage(MSG_AUTH);
+                       lm.add(FIELD_AUTH_PLAYER, user);
+                       lm.add(FIELD_AUTH_PASS, pass);
+                       send(lm);
+
                } catch (Exception e) {
                        log("Error connecting to server: " + e.toString() + "\n");
                        return;
@@ -191,460 +216,238 @@ public class Dusk implements Runnable {
                map.removeEntity(entStore);
        }
 
-       /**
-        * Read a . terminated list from server
-        *
-        * @return
-        */
-       List<String> readList() throws IOException {
-               List<String> l = new ArrayList<>();
-
-               String s = instream.readLine();
-               while (!s.equals(".")) {
-                       l.add(s);
-                       s = instream.readLine();
-               }
-               return l;
-       }
-
-       String readContent(String end) throws IOException {
-               StringBuilder sb = new StringBuilder();
-               String s = instream.readLine();
-
-               while (!s.equals(end)) {
-                       sb.append(s).append('\n');
-                       s = instream.readLine();
-               }
-               return sb.toString();
-       }
-
        //Thread to process incoming commands
        public void run() {
                int incoming = 0;
                while (incoming != -1) {
                        try {
-                               // Handle incoming messages from Server
-//                 incoming = stmIn.readByte();
-                               incoming = instream.read();
+                               DuskMessage dm = DuskMessage.receiveMessage(instream);
 
-                               if (incoming == -1) {
-                                       continue;
-                               }
+                               // debug
+                               System.out.print(state + ": ");
+                               dm.format(System.out);
 
-                               MessageType type = MessageType.fromServer(incoming);
-
-                               System.out.println("state " + state + " incoming = " + type);
-
-                               // Hack to auto-login to server
-                               switch (state) {
-                                       case Username:
-                                               if (type == MessageType.Chat) {
-                                                       String s = instream.readLine();
-                                                       System.out.println("username: text: " + s);
-                                                       if (s.equals("enter your password:")) {
-                                                               state = State.Password;
-                                                               command(pass);
-                                                       } else if (s.endsWith("Is that correct? (yes/no)")) {
-                                                               // "new user" shit
-                                                               state = state.Create;
-                                                               command("yes");
-                                                       }
-                                               }
-                                               continue;
-                                       case Password:
-                                               if (type == MessageType.Chat) {
-                                                       String s = instream.readLine();
-                                                       System.out.println("password: text: " + s);
-                                                       if (s.equals("Login Accepted.")) {
-                                                               state = State.Ready;
+                               switch (dm.name) {
+                                       case MSG_AUTH: {
+                                               ListMessage am = (ListMessage) dm;
+                                               EntityIntegerMessage res = (EntityIntegerMessage) am.get(FIELD_AUTH_RESULT);
+
+                                               switch (res.value) {
+                                                       case AUTH_LOGIN_OK: {
+                                                               log(am.getString(FIELD_AUTH_REASON) + "\n");
                                                                frame.loginOk();
-                                                       } else if (s.equals("Incorrect Password.")) {
-                                                               // blah blah what now?
-                                                               state = state.Connected;
-                                                       }
-                                               }
-                                               continue;
-                                       case Create:
-                                               if (type == MessageType.Chat) {
-                                                       String s = instream.readLine();
-                                                       System.out.println("create: text: " + s);
-                                                       if (s.equals("Enter a new password:")) {
-                                                               command(pass);
-                                                       } else if (s.equals("Confirm that password:")) {
-                                                               //state = State.Password;
-                                                               command(pass);
-                                                       } else if (s.equals("Login Accepted.")) {
-                                                               // this should now ask for a race
+                                                               status.id = res.id;
                                                                state = State.Ready;
-                                                               frame.loginOk();
+                                                               break;
+                                                       }
+                                                       case AUTH_LOGIN_FAILED: {
+                                                               state = State.Username;
+                                                               log(am.getString(FIELD_AUTH_REASON) + "\n");
+                                                               //frame.loginError();
+                                                               break;
+                                                       }
+                                                       case AUTH_LOGIN_EXISTS: {
+                                                               state = State.Username;
+                                                               log(am.getString(FIELD_AUTH_REASON) + "\n");
+                                                               //frame.loginError();
+                                                               break;
+                                                       }
+                                                       case AUTH_LOGIN_INCOMPLETE: {
+                                                               state = State.Username;
+                                                               log(am.getString(FIELD_AUTH_REASON) + "\n");
+
+                                                               pendingQueries = (ListMessage) am.get(FIELD_AUTH_NEWPLAYER);
+                                                               pendingQueryIndex = 0;
+
+                                                               queryMessage = new ListMessage(MSG_AUTH);
+                                                               queryMessage.add(FIELD_AUTH_PLAYER, user);
+                                                               queryMessage.add(FIELD_AUTH_PASS, pass);
+                                                               queryFields = new ListMessage(FIELD_AUTH_NEWPLAYER);
+                                                               queryMessage.add(queryFields);
+
+                                                               nextQuery();
+                                                               break;
                                                        }
-                                                       // TODO: find out what happens if that doesn't work
-                                               }
-                                               continue;
-                                       case SelectRace:
-                                               switch (type) {
-                                                       case ChooseRace:
-                                                               // Bad choice, try again
-                                                               handleChooseRace(instream);
-                                                               continue;
-                                                       default:
-                                                               state = State.Ready;
-                                                       // any other message drop through to process
                                                }
-                                               break;
-                               }
 
-                               switch (type) {
-                                       case Quit: //Quit
-                                       {
+                                               break;
+                                       }
+                                       /**
+                                        * Notify only
+                                        * One ping and one ping only.
+                                        */
+                                       case MSG_PING:
+                                               ping();
+                                               break;
+                                       /**
+                                        * Notify only
+                                        * Server dumped you
+                                        */
+                                       case MSG_QUIT:
                                                loaded = false;
                                                connected = false;
                                                socket.close();
+                                               return;
+                                       case MSG_QUERY: {
+                                               EntityListMessage query = (EntityListMessage) dm;
+                                               EntityListMessage response = new EntityListMessage(query.id, MSG_QUERY);
+
+                                               // Not imjplemented yet, just return a dummy message and let the server deal with it
+                                               // it will work like the login NEWPLAYER shit
+
+                                               send(response);
+
                                                return;
                                        }
-                                       case UpdateImages: //update Images
-                                       {
-                                               rcLocation = instream.readLine();
-                                               // FIXME: not if applet or something?
+                                       case MSG_CLEAR_FLAGS:
+                                               synchronized (map) {
+                                                       for (Entity e : map.getEntities()) {
+                                                               e.intFlag = 0;
+                                                       }
+                                               }
+                                               update();
+                                               break;
+
+                                       case MSG_INIT_MAP: {
+                                               ListMessage lm = (ListMessage) dm;
+
+                                               // FIXME: use jar stuff
+                                               rcLocation = lm.getString(FIELD_MAP_ASSETLOCATION);
+                                               // FIXME: hack
                                                rcLocation = "rc/" + rcLocation;
 
                                                frame.setImages(new File(rcLocation + "/images/map.gif").toURI().toString(), 32,
                                                                new File(rcLocation + "/images/players.gif").toURI().toString(), 64,
                                                                new File(rcLocation + "/images/sprites.gif").toURI().toString(), 64);
-                                               update();
+
+                                               mapSize = lm.getInteger(FIELD_MAP_WIDTH);
+                                               map = new ClientMap(mapSize, mapSize);
                                                break;
                                        }
-                                       case UpdateLocMap: {
-                                               /*
-                                                status.updateLocation(instream);
-                                                System.out.printf("Updating map to %d %d map size %d %d\n",
-                                                status.locx, status.locy, mapSize, mapSize);
-                                                map.updateTiles(status.locx - (map.cols - 1) / 2, status.locy - (map.rows - 1) / 2, instream);
-                                                */
-                                               ServerMessage.MapMessage msg = ServerMessage.MapMessage.decode(instream);
-                                               status.updateLocation(msg.x, msg.y);
-                                               map.updateTiles(msg.x - (map.cols - 1) / 2, msg.y - (map.rows - 1) / 2, msg.map);
+                                       case MSG_UPDATE_MAP: {
+                                               MapMessage mm = (MapMessage) dm;
+
+                                               status.updateLocation(mm.x, mm.y);
+                                               map.updateTiles(mm.x - (map.cols - 1) / 2, mm.y - (map.rows - 1) / 2, mm.map);
                                                update();
                                                frame.setStatus(status);
                                                //frame.info.setText("HP: " + inthp + "/" + intmaxhp + " MP: " + intsp + "/" + intmaxsp + " Loc: " + LocX + "/" + LocY);
                                                buyList.clear();
                                                reloadChoiceLookGetAttack();
-                                               //paint();
+                                               
+                                               // Perhaps i need a status message to go from 'starting' to 'ready'
+                                               loaded = true;
                                                break;
                                        }
-                                       case Chat: //incoming chat
-                                       {
-                                               chat(instream.readLine() + "\n");
+
+                                       case MSG_CHAT:
+                                               chat(((StringMessage) dm).value + "\n");
                                                break;
-                                       }
-                                       case AddEntity: //add Entity
-                                       {
-                                               /*
-                                                String s = instream.readLine();
-                                                int etype = Byte.parseByte(instream.readLine());
-                                                Entity ent = null;
-                                                if (etype == 0) {
-                                                try {
-                                                ent = new Entity(s,
-                                                Long.parseLong(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                etype);
-                                                } catch (NullPointerException e) {
-                                                ent = null;
-                                                }
-                                                } else {
-                                                try {
-                                                ent = new Entity(s,
-                                                Long.parseLong(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                Integer.parseInt(instream.readLine()),
-                                                -1,
-                                                etype);
-                                                } catch (NullPointerException e) {
-                                                ent = null;
-                                                }
-                                                }*/
-                                               EntityMessage msg = ServerMessage.EntityMessage.decode(type, instream);
-                                               if (msg != null) {
-                                                       addEntity(new Entity(msg));
-                                               }
+                                       case MSG_ADD_ENTITY:
+                                               addEntity(new Entity((EntityUpdateMessage) dm));
                                                reloadChoiceLookGetAttack();
                                                update();
                                                break;
-                                       }
-                                       case UpdateStats: //update Stats
-                                       {
-                                               status.updateStatus(instream);
-                                               frame.setStatus(status);
-                                               break;
-                                       }
-                                       case UpdateItems: //update Items
-                                       {
-                                               worn.updateAvailable(instream);
-                                               frame.setDropList(worn.all);
-                                               frame.setEquipment(worn);
-                                               break;
-                                       }
-                                       case UpdateEquipment: //update Equipment
-                                       {
-                                               worn.updateWorn(instream);
-                                               frame.setEquipment(worn);
-                                               break;
-                                       }
-                                       case UpdateInfo: //update Stats
-                                       {
-                                               try {
-                                                       StringBuilder sb = new StringBuilder();
-                                                       String s = instream.readLine();
-                                                       while (!s.equals(".")) {
-                                                               sb.append(s);
-                                                               sb.append("\n");
-                                                               s = instream.readLine();
+                                       case MSG_UPDATE_ENTITY: {
+                                               // fixme: put on entity
+                                               EntityListMessage el = (EntityListMessage) dm;
+                                               Entity e = map.getEntity(el.id);
+                                               if (e != null) {
+                                                       for (DuskMessage m : el.value) {
+                                                               switch (m.name) {
+                                                                       case FIELD_ENTITY_FLAGS:
+                                                                               e.intFlag = ((IntegerMessage) m).value;
+                                                                               break;
+                                                               }
                                                        }
-                                                       frame.setStats(sb.toString());
-                                               } catch (Exception e) {
-                                                       System.err.println("Error loading stats" + e.toString());
+                                                       update();
+                                               } else {
+                                                       log("Error: set flag on unknown entity: " + el.id);
                                                }
                                                break;
                                        }
-                                       case Halt: //halt
-                                       {
-                                               loaded = false;
-                                               break;
-                                       }
-                                       case UpdateActions: //update Actions
-                                       {
-                                               frame.setActionList(readList());
+                                       case MSG_REMOVE_ENTITY:
+                                               removeEntity(((EntityMessage) dm).id);
+                                               reloadChoiceLookGetAttack();
+                                               update();
                                                break;
-                                       }
-                                       case LoadMusic: //load music
-                                       {
-                                               try {
-                                                       // FIXME; applet
-                                                       /*
-                                                        if (blnApplet) {
-                                                        log("Loading music.\n");
-                                                        intMusicTypes = Integer.parseInt(instream.readLine());
-                                                        audMusic = new AudioClip[intMusicTypes][];
-                                                        intNumSongs = new int[intMusicTypes];
-                                                        for (intStore = 0; intStore < intMusicTypes; intStore++) {
-                                                        intNumSongs[intStore] = Integer.parseInt(instream.readLine());
-                                                        audMusic[intStore] = new AudioClip[intNumSongs[intStore]];
-                                                        for (intStore2 = 0; intStore2 < intNumSongs[intStore]; intStore2++) {
-                                                        String s = instream.readLine();
-                                                        try {
-                                                        //audMusic[intStore][intStore2] = appShell.getAudioClip(new URL(strStore));
-                                                        while (audMusic[intStore][intStore2] == null) {
-                                                        }
-                                                        } catch (Exception e) {
-                                                        System.err.println("Error while trying to load music file " + s + ":" + e.toString());
-                                                        }
-                                                        }
-                                                        }
-                                                        log("Music loaded.\n");
-                                                        }*/
-                                               } catch (Exception e) {
-                                                       blnMusic = false;
-                                                       log("Your java virtual machine does not support midi music\n");
-                                                       System.err.println("Error while trying to load music files:" + e.toString());
+                                       case MSG_MOVE: {
+                                               EntityByteMessage mm = (EntityByteMessage) dm;
+
+                                               switch (mm.value) {
+                                                       case 'n':
+                                                               handleMove(mm.id, 1, 0, -1, 0);
+                                                               break;
+                                                       case 's':
+                                                               handleMove(mm.id, 3, 0, 1, 1);
+                                                               break;
+                                                       case 'w':
+                                                               handleMove(mm.id, 5, -1, 0, 2);
+                                                               break;
+                                                       case 'e':
+                                                               handleMove(mm.id, 7, 1, 0, 3);
+                                                               break;
                                                }
                                                break;
                                        }
-                                       case PlayMusic: //play music
-                                       {
-                                               if (blnMusic) {
-                                                       /* FIXME: sound
-                                                        try {
-                                                        intStore = Integer.parseInt(instream.readLine());
-                                                        if (audMusicPlaying != null) {
-                                                        audMusicPlaying.stop();
-                                                        }
-                                                        audMusicPlaying = audMusic[intStore][(int) (Math.random() * intNumSongs[intStore])];
-                                                        audMusicPlaying.loop();
-                                                        } catch (Exception e) {
-                                                        System.err.println("Error while trying to play music file:" + e.toString());
-                                                        }*/
-                                               }
+                                       case MSG_UPDATE_ACTIONS:
+                                               frame.setActionList(((StringListMessage) dm).value);
                                                break;
-                                       }
-                                       case Ping: //stillThere?
-                                       {
-                                               outstream.writeBytes("notdead\n");
+                                       case MSG_INVENTORY:
+                                               sellList = ((TransactionMessage) dm).items;
+
+                                               // fixme: merge inventory
+                                               frame.setSellList(sellList);
+                                               worn.updateAvailable(sellList);
+                                               frame.setDropList(worn.all);
+                                               frame.setEquipment(worn);
                                                break;
-                                       }
-                                       case Proceed: //proceed
-                                       {
-                                               status.id = Long.parseLong(instream.readLine());
-                                               loaded = true;
+                                       case MSG_EQUIPMENT:
+                                               worn.updateWorn((ListMessage) dm);
+                                               frame.setEquipment(worn);
                                                break;
-                                       }
-                                       case PlaySound: //play sfx
-                                       {
-                                               try {
-                                                       //audSFX[Integer.parseInt(instream.readLine())].play();
-                                               } catch (Exception e) {
+                                       case MSG_INFO_PLAYER: {
+                                               // FIXME: status will include all the other shit about the player below too
+                                               status.updateStatus((ListMessage) dm);
+                                               frame.setStatus(status);
+
+                                               StringBuilder sb = new StringBuilder();
+                                               // HACK: don't bother showing this shit yet
+                                               ListMessage lm = (ListMessage) dm;
+                                               for (DuskMessage d : lm.value) {
+                                                       sb.append(d).append('\n');
                                                }
+                                               frame.setStats(sb.toString());
                                                break;
                                        }
-                                       case RemoveEntity: //remove entity
-                                       {
-                                               long id = Long.valueOf(instream.readLine()).longValue();
-
-                                               removeEntity(id);
-                                               reloadChoiceLookGetAttack();
-                                               update();
+                                       case MSG_INFO_PET: {
+                                               // FIXME: Same data as above but for any pet attached
+                                               System.out.println("Update pet ignored");
                                                break;
                                        }
-                                       case UpdateMerchant: //update Merchant
-                                       {
-                                               buyList = TransactionItem.createItems(readList());
+                                       case MSG_UPDATE_MERCHANT:
+                                               buyList = ((TransactionMessage) dm).items;
                                                frame.setBuyList(buyList);
                                                break;
-                                       }
-                                       case EditText: //view/edit
-                                       {
-                                               String name = instream.readLine();
-                                               frame.visitFile(name, readContent("--EOF--"), true);
-                                               break;
-                                       }
-                                       case ResizeMap: //resize Map
-                                       {
-                                               mapSize = Integer.parseInt(instream.readLine());
-                                               map = new ClientMap(mapSize, mapSize);
-                                               if (blnApplet) {
-                                                       outstream.writeBytes("appletimages\n");
-                                               } else {
-                                                       outstream.writeBytes("applicationimages\n");
-                                               }
-                                               //scaleWindow();
-                                               break;
-                                       }
-                                       case ViewText: //view/no-edit
-                                       {
-                                               String name = instream.readLine();
-                                               frame.visitFile(name, readContent("--EOF--"), false);
-                                               break;
-                                       }
-                                       case ExitMerchant: //offMerchant
-                                       {
+                                       case MSG_EXIT_MERCHANT:
                                                frame.exitShop();
                                                buyList.clear();
                                                sellList.clear();
                                                break;
-                                       }
-                                       case UpdateSell: //update Sell
-                                       {
-                                               sellList = TransactionItem.createItems(readList());
-                                               frame.setSellList(sellList);
-                                               break;
-                                       }
-                                       case ColourChat: //colour chat
-                                       {
-                                               // FIXME: re-implement colour, or probably fix it (hex codes)
-                                               instream.readLine();
-                                               instream.readLine();
-                                               instream.readLine();
-                                               chat(instream.readLine() + "\n");
-                                               break;
-                                       }
-                                       case MoveNorth: //move north
-                                       {
-                                               long ID = Long.parseLong(instream.readLine());
-
-                                               handleMove(ID, 1, 0, -1, 0);
-                                               break;
-                                       }
-                                       case MoveSouth: //move south
-                                       {
-                                               long ID = Long.parseLong(instream.readLine());
-
-                                               handleMove(ID, 3, 0, 1, 1);
-                                               break;
-                                       }
-                                       case MoveWest: //move west
-                                       {
-                                               long ID = Long.parseLong(instream.readLine());
-
-                                               handleMove(ID, 5, -1, 0, 2);
-                                               break;
-                                       }
-                                       case MoveEast: //move east
-                                       {
-                                               long ID = Long.parseLong(instream.readLine());
+                                       case MSG_BATTLE_START: {
+                                               ListMessage lm = (ListMessage) dm;
 
-                                               handleMove(ID, 7, 1, 0, 3);
+                                               battle("Start battling: " + lm.getString(FIELD_BATTLE_OPPONENT));
                                                break;
                                        }
-                                       case UpdateRange: //update range
-                                       {
-                                               status.range = Integer.parseInt(instream.readLine());
-                                               break;
-                                       }
-                                       case SetFlag: //set flag
-                                       {
-                                               long id = Long.valueOf(instream.readLine()).longValue();
-                                               int flags = Integer.parseInt(instream.readLine());
-
-                                               Entity e = map.getEntity(id);
-                                               if (e != null) {
-                                                       e.intFlag = flags;
-                                                       update();
-                                               } else {
-                                                       log("Error: set flag on unknown entity: " + id);
-                                               }
-                                               break;
-                                       }
-                                       case ClearFlags: //clear all flags
-                                       {
-                                               // FIXME: hideinfo?
-                                               synchronized (map) {
-                                                       for (Entity e : map.getEntities()) {
-                                                               e.intFlag = 0;
-                                                       }
-                                               }
-                                               update();
-                                               break;
-                                       }
-                                       case StartBattle: //show battle window and clear text
-                                       {
-                                               // FIXME: no battle popup window at the moment
-                                               String s = instream.readLine();
-                                               battle("Battle started: " + s + "\n");
-                                               break;
-                                       }
-                                       case UpdateBattle: //show battle window and update title
-                                       {
-                                               // FIXME: no battle popup window at the moment
-                                               String s = instream.readLine();
-                                               battle("Battle status: " + s + "\n");
-                                               break;
-                                       }
-                                       case LogBattle: //add text to battle window
-                                       {
-                                               // FIXME: no battle popup window at the moment
-                                               String s = instream.readLine();
-                                               battle(s + "\n");
-                                               break;
-                                       }
-                                       case ChooseRace:
-                                               handleChooseRace(instream);
-                                               break;
-                                       case HitEntity: {
-                                               long toID = Long.valueOf(instream.readLine());
-                                               int delta = -Integer.valueOf(instream.readLine());
-                                               int newhp = Integer.valueOf(instream.readLine());
-                                               int tothp = Integer.valueOf(instream.readLine());
-                                               long fromID = Long.valueOf(instream.readLine());
-                                               String how = instream.readLine();
-
-                                               String s;
-                                               do {
-                                                       s = instream.readLine();
-                                               } while (!s.equals("."));
+                                       case MSG_BATTLE_UPDATE: {
+                                               ListMessage lm = (ListMessage) dm;
+                                               long toID = lm.getLong(FIELD_BATTLE_TARGET);
+                                               int delta = -lm.getInteger(FIELD_BATTLE_DAMAGE);
+                                               int newhp = lm.getInteger(FIELD_BATTLE_HP);
+                                               int tothp = lm.getInteger(FIELD_BATTLE_MAXHP);
+                                               long fromID = lm.getLong(FIELD_BATTLE_SOURCE);
+                                               String how = lm.getString(FIELD_BATTLE_WHAT);
 
                                                System.out.printf("entity %d %s delta %d hp %d/%d\n",
                                                                toID, how, delta, newhp, tothp);
@@ -669,14 +472,30 @@ public class Dusk implements Runnable {
 
                                                break;
                                        }
+                                       case MSG_BATTLE_CHAT:
+                                               battle(((StringMessage) dm).value + "\n");
+                                               break;
+                                       case MSG_VIEW_TEXT: {
+                                               ListMessage lm = (ListMessage) dm;
+                                               System.out.println("View: " + lm.getString(FIELD_TEXT_NAME, ""));
+                                               System.out.println("Edit: " + lm.getByte(FIELD_TEXT_EDITABLE, (byte) 0));
+                                               System.out.println("Text: " + lm.getString(FIELD_TEXT_TEXT, ""));
+                                               break;
+                                       }
                                }
-                       } catch (Exception e) {
+                       } catch (ClassCastException e) {
+                               e.printStackTrace(System.out);
+                               // ignore this, protocol broken it should recover
+                       } catch (IOException e) {
                                System.err.println("Error at run() with value " + incoming + " : " + e.toString());
                                e.printStackTrace(System.out);
 
                                log("Error at run() with value " + incoming + " : " + e.toString() + "\n");
                                connected = false;
                                return;
+                       } catch (Exception e) {
+                               e.printStackTrace(System.out);
+                               // ignore this, client broken, it might recover
                        }
                }
                System.err.println("Error at run() with value " + incoming);
@@ -713,22 +532,29 @@ public class Dusk implements Runnable {
                }
        }
 
-       private void handleChooseRace(DataInputStream in) throws IOException {
-               List<String> races = new ArrayList<>();
-               String prompt = in.readLine();
-               String l = in.readLine();
-               while (!l.equals(".")) {
-                       races.add(l);
-                       l = in.readLine();
+       synchronized void nextQuery() {
+               if (pendingQueries == null)
+                       return;
+
+               if ((pendingQueryIndex < pendingQueries.value.size())) {
+                       ListMessage lm = (ListMessage) pendingQueries.value.get(pendingQueryIndex++);
+
+                       queryID = lm.name;
+                       frame.chooseRace(lm.getString(FIELD_QUERY_PROMPT), lm.getStringList(FIELD_QUERY_OPTIONS));
+               } else {
+                       try {
+                               send(queryMessage);
+                       } catch (IOException ex) {
+                               Logger.getLogger(Dusk.class.getName()).log(Level.SEVERE, null, ex);
+                       }
                }
-               state = State.SelectRace;
-               frame.chooseRace(prompt, races);
        }
 
+       // This currently a generic async response to a list query
+       // FIXME; rename
        public void chooseRace(String race) {
-               if (state == State.SelectRace) {
-                       command(race);
-               }
+               queryFields.add(queryID, race);
+               nextQuery();
        }
 
        // TBD - use the individual functions
@@ -854,7 +680,7 @@ public class Dusk implements Runnable {
        public void logout() {
                try {
                        if (connected) {
-                               outstream.writeBytes("quit\n");
+                               command("quit");
                        }
                } catch (Exception exc) {
                }
@@ -869,7 +695,7 @@ public class Dusk implements Runnable {
        public void quit() {
                try {
                        if (connected) {
-                               outstream.writeBytes("quit\n");
+                               command("quit");
                        }
                } catch (Exception exc) {
                }
@@ -894,10 +720,15 @@ public class Dusk implements Runnable {
                }
        }
 
+       private void send(DuskMessage lm) throws IOException {
+               System.out.print("sending: ");
+               lm.format(System.out);
+               lm.sendMessage(outstream);
+       }
+
        private void docmd(String cmd) {
-               System.out.println("send command: " + cmd);
                try {
-                       outstream.writeBytes(cmd);
+                       send(new StringMessage(MSG_COMMAND, cmd));
                } catch (IOException ex) {
                        // TODO: close connection here
                        Logger.getLogger(Dusk.class.getName()).log(Level.SEVERE, null, ex);
@@ -905,11 +736,11 @@ public class Dusk implements Runnable {
        }
 
        public void command(String what) {
-               docmd(what + "\n");
+               docmd(what);
        }
 
        public void command(String what, String params) {
-               docmd(what + " " + params + "\n");
+               docmd(what + " " + params);
        }
 
        public void move(Direction dir) {
@@ -919,8 +750,7 @@ public class Dusk implements Runnable {
        public void moveTo(int dx, int dy) {
                //Move to location
                try {
-                       System.out.println("goto " + dx + "," + dy);
-                       outstream.writeBytes("goto " + dx + " " + dy + "\n");
+                       command("goto " + dx + " " + dy);
                } catch (Exception e) {
                        frame.log("Error in goto %d,%d: %s", dx, dy, e);
                }
@@ -934,7 +764,10 @@ public class Dusk implements Runnable {
                command("sell " + quantity + " " + item.name);
        }
 
-       // TODO: find out why these uses the base name and ignores the numbers.
+       private void ping() {
+               command("notdead");
+       }
+
        public void attack(Entity e) {
                command("a " + e.ID);
        }
index f141c10..2e3bdbd 100644 (file)
@@ -24,7 +24,7 @@
  */
 package duskz.client;
 
-import duskz.protocol.ServerMessage;
+import duskz.protocol.EntityUpdateMessage;
 
 public class Entity {
 
@@ -62,8 +62,8 @@ public class Entity {
                intType = inintType;
        }
 
-       public Entity(ServerMessage.EntityMessage msg) {
-               strName = msg.name;
+       public Entity(EntityUpdateMessage msg) {
+               strName = msg.entityName;
                locx = msg.x;
                locy = msg.y;
                ID = msg.id;
index b988c77..7472473 100644 (file)
  */
 package duskz.client;
 
+import duskz.protocol.DuskMessage;
+import duskz.protocol.DuskMessage.StringMessage;
+import duskz.protocol.ListMessage;
+import duskz.protocol.TransactionItem;
 import duskz.protocol.Wearing;
-import java.io.DataInputStream;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
 /**
  * This is a bit junk.
- * Server code also has a similar mess
+ * FIXME: merge with inventory "sell list"
  *
  * @author notzed
  */
@@ -80,38 +82,48 @@ public class Equipment implements Wearing {
                return -1;
        }
 
-       /**
-        * Decode worn from network
-        *
-        * @return
-        */
-       public void updateWorn(DataInputStream instream) throws IOException {
-               for (int i = 0; i < worn.length; i++) {
-                       worn[i] = instream.readLine();
-                       System.out.println(" worn: " + worn[i]);
+       public void updateWorn(ListMessage lm) {
+               for (int i=0;i<worn.length;i++)
+                       worn[i] = "none";
+               
+               for (DuskMessage dm: lm.value) {
+                       StringMessage sm = (StringMessage) dm;
+                       
+                       worn[sm.name] = sm.value;
                }
        }
-
-       public void updateAvailable(DataInputStream instream) throws IOException, NumberFormatException {
-               String line;
-
+       public void updateAvailable(ListMessage lm) {
                all.clear();
                for (int i = 0; i < available.length; i++)
                        available[i].clear();
                wearableAt.clear();
-
-               while ((line = instream.readLine()) != null && !line.equals(".")) {
-                       // Need to map from server 'id' to our index
-                       int i = Integer.parseInt(line) - 1;
-                       String s = instream.readLine();
-
-                       System.out.println(" " + i + " " + s);
-
+               
+               for (DuskMessage dm: lm.value) {
+                       StringMessage sm = (StringMessage) dm;
+                       
+                       // remap to local id;
+                       int i = sm.name - 1;
                        if (i >= 0 && i < available.length) {
-                               available[i].add(s);
-                               wearableAt.put(s, i);
+                               available[i].add(sm.value);
+                               wearableAt.put(sm.value, i);
+                       }
+                       all.add(sm.value);
+               }
+       }
+       
+       public void updateAvailable(List<TransactionItem> list) {
+               all.clear();
+               for (int i = 0; i < available.length; i++)
+                       available[i].clear();
+               wearableAt.clear();
+               
+               for (TransactionItem item: list) {
+                       if (item.type >= 0 && item.type < available.length) {
+                               available[item.type].add(item.name);
+                               wearableAt.put(item.name, item.type);
                        }
-                       all.add(s);
+                       all.add(item.name);
                }
+               
        }
 }
index a6dace1..9bd3490 100644 (file)
@@ -21,6 +21,7 @@
  */
 package duskz.client;
 
+import duskz.protocol.TransactionItem;
 import java.util.List;
 
 /**
index a6efd0e..35d4ce9 100644 (file)
  */
 package duskz.client;
 
-import java.io.DataInputStream;
+import duskz.protocol.DuskMessage;
+import duskz.protocol.DuskMessage.IntegerMessage;
+import duskz.protocol.DuskProtocol;
+import duskz.protocol.ListMessage;
 import java.io.IOException;
 
 /**
@@ -33,7 +36,7 @@ import java.io.IOException;
  *
  * @author notzed
  */
-public class Status {
+public class Status implements DuskProtocol {
 
        long id;
        // Attack range
@@ -42,11 +45,27 @@ public class Status {
        int mp, maxmp;
        int locx, locy;
 
-       public void updateStatus(DataInputStream instream) throws IOException {
-               hp = Integer.parseInt(instream.readLine());
-               maxhp = Integer.parseInt(instream.readLine());
-               mp = Integer.parseInt(instream.readLine());
-               maxmp = Integer.parseInt(instream.readLine());
+       public void updateStatus(ListMessage lm) throws IOException {
+               // FIXME: int/dex and all that junk
+               for (DuskMessage a : lm.value) {
+                       switch (a.name) {
+                               case FIELD_INFO_HP:
+                                       hp = ((IntegerMessage) a).value;
+                                       break;
+                               case FIELD_INFO_MAXHP:
+                                       maxhp = ((IntegerMessage) a).value;
+                                       break;
+                               case FIELD_INFO_MP:
+                                       mp = ((IntegerMessage) a).value;
+                                       break;
+                               case FIELD_INFO_MAXMP:
+                                       maxmp = ((IntegerMessage) a).value;
+                                       break;
+                               case FIELD_INFO_RANGE:
+                                       range = ((IntegerMessage) a).value;
+                                       break;
+                       }
+               }
        }
 
        public void updateLocation(int x, int y) {
@@ -54,13 +73,6 @@ public class Status {
                locy = y;
        }
 
-       public void updateLocation(DataInputStream instream) throws IOException {
-               //locx = Integer.parseInt(instream.readLine());
-               //locy = Integer.parseInt(instream.readLine());
-               locx = instream.readShort();
-               locy = instream.readShort();
-       }
-
        @Override
        public String toString() {
                return "HP: " + hp + "/" + maxhp + " MP: " + mp + "/" + maxmp + " Loc: " + locx + "/" + locy;
diff --git a/DuskZ/src/duskz/client/TransactionItem.java b/DuskZ/src/duskz/client/TransactionItem.java
deleted file mode 100644 (file)
index 910b873..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * This file is part of DuskZ, a graphical mud engine.
- *
- * Copyright (C) 2013 Michael Zucchi <notzed@gmail.com>
- *
- * DuskZ is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * DuskZ 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with DuskZ.  If not, see <http://www.gnu.org/licenses/>.
- */
-package duskz.client;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Takes a server response and parses it into a name/cost/count
- * list suitable for a toolkit.
- *
- * @deprecated should be merged with DuskCommon's TransactionItem
- * @author notzed
- */
-@Deprecated
-public class TransactionItem implements Comparable<TransactionItem> {
-
-       public String cost;
-       public int count;
-       public String name;
-
-       /**
-        * Convert a list of items as from the server into a list of
-        * transaction items.
-        * i.e. count them up, separate cost.
-        *
-        * @param source
-        * @return
-        */
-       public static List<TransactionItem> createItems(List<String> source) {
-               List<TransactionItem> list = new ArrayList<>();
-
-               Collections.sort(source);
-               TransactionItem last = null;
-               String lastName = null;
-               for (String s : source) {
-                       if (lastName != null && lastName.equals(s)) {
-                               last.count++;
-                       } else {
-                               int i;
-
-                               last = new TransactionItem();
-                               last.count = 1;
-
-                               i = s.indexOf(')');
-                               if (i != -1) {
-                                       last.cost = s.substring(0, i);
-                                       last.name = s.substring(i + 1);
-                               } else {
-                                       last.name = s;
-                                       last.cost = "";
-                               }
-                               list.add(last);
-                       }
-                       lastName = s;
-               }
-               Collections.sort(list);
-               return list;
-       }
-
-       public String getName() {
-               return name;
-       }
-
-       public int getCount() {
-               return count;
-       }
-
-       public String getCost() {
-               return cost;
-       }
-
-       @Override
-       public String toString() {
-               return cost + ") " + name + "[" + count + "]";
-       }
-
-       @Override
-       public int compareTo(TransactionItem t) {
-               return name.compareTo(t.name);
-       }
-}
index be60d02..60823f7 100644 (file)
@@ -34,7 +34,7 @@ import duskz.client.Entity;
 import duskz.client.Equipment;
 import duskz.client.GUI;
 import duskz.client.Status;
-import duskz.client.TransactionItem;
+import duskz.protocol.TransactionItem;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.ArrayList;
index 733bdf9..637b6f6 100644 (file)
@@ -21,7 +21,7 @@
  */
 package duskz.client.fx;
 
-import duskz.client.TransactionItem;
+import duskz.protocol.TransactionItem;
 import java.util.List;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
@@ -95,7 +95,7 @@ public class TransactionPane extends HBox {
                nameCol.setCellValueFactory(new PropertyValueFactory("name"));
 
                TableColumn<TransactionItem, String> costCol = new TableColumn<TransactionItem, String>("Cost");
-               costCol.setCellValueFactory(new PropertyValueFactory("cost"));
+               costCol.setCellValueFactory(new PropertyValueFactory("costText"));
 
                table.getColumns().setAll(countCol, nameCol, costCol);