--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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());
- }
- }
- }
-}
*/
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;
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;
}
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();
+ }
}
--- /dev/null
+/*
+ * 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);
+ }
+}
/**
* 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 = {
--- /dev/null
+/*
+ * 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 {
+}
/**
* 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;
*
* @author Tom Weingarten
*/
-public class Battle {
+public class Battle implements DuskProtocol {
private ArrayList<LivingThing> vctSide1 = new ArrayList<>(),
vctSide2 = new ArrayList<>();
}
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);
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);
}
msg += ".";
}
- chatMessage("\t" + msg);
+ chatMessage(msg);
}
return true;
}
void endBattle() {
blnRunning = false;
- chatMessage("\tYou have won the battle.");
+ chatMessage("You have won the battle.");
endBattle(vctSide1);
endBattle(vctSide2);
}
}
}
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();
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;
}
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;
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);
}
}
}
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);
}
}
}
* @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);
}
/**
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();
}
} 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);
/**
}
}
} 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();
--- /dev/null
+/*
+ * 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);
+ }
+
+}
/**
* 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;
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) {
// 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": {
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.";
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);
}
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;
*/
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);
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.";
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();
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();
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")) {
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;
}
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.");
game.log.printError("parseCommand():While " + lt.name + " was changing their password", e);
}
lt.proceed();
+ */
}
case "wear": {
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;
* 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;
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;
*
* @author Tom Weingarten
*/
-public class DuskEngine implements Runnable {
+public class DuskEngine implements Runnable, DuskProtocol {
//Logger
public Log log;
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;
*/
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,
public DuskEngine() {
RandomAccessFile rafFile = null;
varVariables = new VariableSet();
- varIP = new VariableSet();
log = new Log(System.out);
try {
oldviewrange = viewrange;
mapsize = 1 + (2 * viewrange);
for (LivingThing thnStore : playersByName.values()) {
- thnStore.resizeMap();
+ thnStore.initMap();
}
}
//Load Triggered Scripts
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();
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());
+ // }
+ //}
}
}
}
synchronized void resizeMap(int x, int y) {
map.resize(x, y);
for (LivingThing lt : playersByName.values()) {
- lt.resizeMap();
+ lt.initMap();
}
blnMapHasChanged = true;
}
* @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()) {
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);
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);
+ }
+ }
}
}
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...
} 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: {
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: {
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: {
}
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
* 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
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?
/**
* 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;
/**
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];
}
*/
package duskz.server.entity;
+import duskz.protocol.Wearing;
import duskz.server.DuskEngine;
import duskz.server.Script;
import java.io.File;
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
-/*
+ /*
* 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;
*
* @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?
//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
}
/**
- * 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();
}
@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>");
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;
}
/**
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;
// 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);
}
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();
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);
}
/**
* Train a skill or spell.
*
- * @param name
+ * @param entityName
* @param add increment
* @return true if the skill/spell exists and was created/incremented
*/
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);
}
}
}
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 {
}
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;
}
}
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;
}
isSaveable = true;
}
+ // TODO: the close commands need merging somehow
public void close() {
if (name == null || name.equalsIgnoreCase("default")) {
return;
isWorking = false;
isSaveable = false;
isSaveNeeded = false;
+
if (!conditions.isEmpty() && game.checkConditionList.contains(this)) {
game.checkConditionList.remove(this);
}
}
}
+ // 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() {
}
// 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
}
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) {
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;
}
|| 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);
}
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++) {
//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
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();
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);
}
}
- 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
{
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;
}
}
- 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)
{
}
}*/
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 {
}
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);
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();
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();
}
}
*/
package duskz.client;
-import java.io.DataInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
&& 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++) {
* 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;
*
* @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,
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;
//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"));
}
State state = State.Unconnected;
String pass;
+ String user;
public void connect(String address, int port, String user, String pass) {
// Connect to Server
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);
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;
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);
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);
}
}
- 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
public void logout() {
try {
if (connected) {
- outstream.writeBytes("quit\n");
+ command("quit");
}
} catch (Exception exc) {
}
public void quit() {
try {
if (connected) {
- outstream.writeBytes("quit\n");
+ command("quit");
}
} catch (Exception exc) {
}
}
}
+ 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);
}
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) {
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);
}
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);
}
*/
package duskz.client;
-import duskz.protocol.ServerMessage;
+import duskz.protocol.EntityUpdateMessage;
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;
*/
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
*/
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);
}
+
}
}
*/
package duskz.client;
+import duskz.protocol.TransactionItem;
import java.util.List;
/**
*/
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;
/**
*
* @author notzed
*/
-public class Status {
+public class Status implements DuskProtocol {
long id;
// Attack range
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) {
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;
+++ /dev/null
-/*
- * 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);
- }
-}
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;
*/
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;
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);