import java.lang.*; import java.io.*; import java.awt.*; class NetrekState implements Cloneable { public final static int MAXTORP = 8; public final static int MAXPLASMA = 1; public final static int MAXPHASER = 1; public final static int MAXPLAYER = 20; public final static int MAXPLANET = 40; public final static int MAXMESSAGE = 50; final static byte SP_MESSAGE = 1; final static byte SP_PLAYER_INFO= 2; /* general player info not elsewh*/ final static byte SP_KILLS = 3; /* # kills a player has */ final static byte SP_PLAYER = 4; /* x,y for player */ final static byte SP_TORP_INFO = 5; /* torp status */ final static byte SP_TORP = 6; /* torp location */ final static byte SP_PHASER = 7; /* phaser status and direction */ final static byte SP_PLASMA_INFO= 8; /* player login information */ final static byte SP_PLASMA = 9; /* like SP_TORP */ final static byte SP_WARNING = 10; /* like SP_MESG */ final static byte SP_MOTD = 11; /* line from .motd screen */ final static byte SP_YOU = 12; /* info on you? */ final static byte SP_QUEUE = 13; /* estimated loc in queue? */ final static byte SP_STATUS = 14; /* galaxy status numbers */ final static byte SP_PLANET = 15; /* planet armies & facilities */ final static byte SP_PICKOK = 16; /* your team & ship was accepted */ final static byte SP_LOGIN = 17; /* login response */ final static byte SP_FLAGS = 18; /* give flags for a player */ final static byte SP_MASK = 19; /* tournament mode mask */ final static byte SP_PSTATUS = 20; /* give status for a player */ final static byte SP_BADVERSION = 21; /* invalid version number */ final static byte SP_HOSTILE = 22; /* hostility settings for a playe*/ final static byte SP_STATS = 23; /* a player's statistics */ final static byte SP_PL_LOGIN = 24; /* new player logs in */ final static byte SP_RESERVED = 25; /* for future use */ final static byte SP_PLANET_LOC = 26; /* planet name, x, y */ final static byte SP_SCAN = 27; /* ATM: results of player scan */ final static byte SP_UDP_REPLY = 28; /* notify client of UDP status */ final static byte SP_SEQUENCE = 29; /* sequence # packet */ final static byte SP_SC_SEQUENCE= 30; /* this trans is semi-critical in*/ final static byte SP_RSA_KEY = 31; /* handles binary verification */ final static byte SP_S_REPLY = 40; /* reply to send-short request */ final static byte SP_S_MESSAGE = 41; /* var. Message Packet */ final static byte SP_S_WARNING = 42; /* Warnings with 4 Bytes */ final static byte SP_S_YOU = 43; /* hostile,armies,whydead,etc .. */ final static byte SP_S_YOU_SS = 44; /* your ship status */ final static byte SP_S_PLAYER = 45; /* variable length player packet */ final static byte SP_PING = 46; /* ping packet */ final static byte SP_S_TORP = 47; /* variable length torp packet */ final static byte SP_S_TORP_INFO= 48; /* SP_S_TORP with TorpInfo */ final static byte SP_S_8_TORP = 49; /* optimized SP_S_TORP */ final static byte SP_S_PLANET = 50; /* see SP_PLANET */ final static byte SP_FEATURE = 60; /* feature packet */ public boolean ready = false; ServerState state; Players players; Phasers phasers; Torps torps; PlasmaTorps plasmas; Planets planets; Messages messages; byte buf[]; byte keymap[]; byte pickok_state; byte pickok_team; byte login_accept; int login_flags; ByteArrayOutputStream features; public NetrekState() { buf = new byte[128]; features = new ByteArrayOutputStream(); state = new ServerState(); players = new Players(MAXPLAYER, state); phasers = new Phasers(players, MAXPHASER); torps = new Torps(players, MAXTORP); plasmas = new PlasmaTorps(players, MAXPLASMA); planets = new Planets(MAXPLANET); messages = new Messages(MAXMESSAGE, players); } public NetrekState copy() { NetrekState ret = null; try { ret = (NetrekState) clone(); } catch(CloneNotSupportedException ex) { } ret.state = state.copy(); ret.players = players.copy(ret.state); ret.phasers = phasers.copy(ret.players); ret.torps = torps.copy(ret.players); ret.plasmas = plasmas.copy(ret.players); ret.planets = planets.copy(); ret.messages = messages.copy(ret.players); return ret; } public boolean step(DataInput data) { byte type; ready = false; if (data == null) return false; //planets.Step(); players.Step(); //phasers.Step(); plasmas.Step(); torps.Step(); while (!ready) { try { type = data.readByte(); switch(type) { case SP_MESSAGE: messages.handleMessage(data); break; case SP_PLAYER_INFO: players.handlePlayerInfo(data); break; case SP_KILLS: players.handleKills(data); break; case SP_PLAYER: players.handlePlayer(data); break; case SP_TORP_INFO: torps.handleTorpInfo(data); break; case SP_TORP: torps.handleTorp(data); break; case SP_PHASER: phasers.handlePhaser(data); break; case SP_PLASMA_INFO: plasmas.handlePlasmaInfo(data); break; case SP_PLASMA: plasmas.handlePlasma(data); break; case SP_WARNING: messages.handleWarning(data); break; case SP_MOTD: handleMOTD(data); break; case SP_YOU: players.handleYou(data, state); break; case SP_QUEUE: handleQueue(data); break; case SP_STATUS: state.handleStatus(data); break; case SP_PLANET: planets.handlePlanet(data); break; case SP_PICKOK: handlePickOK(data); break; // ignore for now case SP_LOGIN: handleLogin(data); break; // ignore for now as well... case SP_FLAGS: players.handleFlags(data); break; case SP_MASK: state.handleMask(data, state); break; case SP_PSTATUS: players.handlePlayerStatus(data); break; // case SP_BADVERSION: case SP_HOSTILE: players.handleHostile(data); break; case SP_STATS: players.handleStats(data); break; case SP_PL_LOGIN: players.handlePlayerLogin(data); break; case SP_RESERVED: handleReserved(data); break; case SP_PLANET_LOC: planets.handlePlLoc(state, data); break; // case SP_SCAN: case SP_UDP_REPLY: handleUDPReply(data); break; case SP_SEQUENCE: handleSequence(data); break; case SP_SC_SEQUENCE: handleSequence(data); break; case SP_RSA_KEY: handleRSAKey(data); break; case SP_S_REPLY: handleSReply(data); break; case SP_S_MESSAGE: messages.handleSMessage(data); break; case SP_S_WARNING: messages.handleSWarning(data); break; case SP_S_YOU: players.handleSYou(data, state); break; case SP_S_YOU_SS: players.handleSYouSS(data, state); break; case SP_S_PLAYER: players.handleSPlayer(data, state); break; case SP_PING: handlePing(data); break; case SP_S_TORP: torps.handleSTorp(data, state); break; case SP_S_TORP_INFO: torps.handleSTorpInfo(data, state); break; case SP_S_8_TORP: torps.handleS8Torp(data, state); break; case SP_S_PLANET: planets.handleSPlanet(data); break; case SP_FEATURE: handleFeature(data); break; case 127: handleSync(data); break; default: System.err.print("Unrecognized packet ID: "); System.err.println(type); return false; } //System.err.println(type); } catch(IOException e) { return false; } } return true; } // struct motd_spacket { // char type; /* SP_MOTD */ // char pad1; // char pad2; // char pad3; // char line[80]; // }; void handleMOTD(DataInput data) throws IOException { byte pad1 = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); byte line[] = new byte[80]; data.readFully(line); } // struct queue_spacket { // char type; /* SP_QUEUE */ // char pad1; // short pos; // }; void handleQueue(DataInput data) throws IOException { byte pad1 = data.readByte(); short pos = data.readShort(); } // struct pickok_spacket { // char type; /* SP_PICKOK */ // char state; // char pad2; // char pad3; // }; void handlePickOK(DataInput data) throws IOException { pickok_state = data.readByte(); pickok_team = data.readByte(); byte pad3 = data.readByte(); } void sendPickOK(DataOutput data) throws IOException { data.writeByte(SP_PICKOK); data.writeByte(pickok_state); data.writeByte(pickok_team); data.writeByte(0); } // struct login_spacket { // char type; /* SP_LOGIN */ // char accept; /* 1/0 */ // char pad2; // char pad3; // LONG flags; // char keymap[96]; // }; void handleLogin(DataInput data) throws IOException { login_accept = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); login_flags = data.readInt(); keymap = new byte[96]; data.readFully(keymap); } void sendLogin(DataOutput data) throws IOException { data.writeByte(SP_LOGIN); data.writeByte(login_accept); data.writeByte(0); data.writeByte(0); data.writeInt(login_flags); data.write(keymap); } // struct reserved_spacket { // char type; /* SP_RESERVED */ // char pad1; // char pad2; // char pad3; // char data[16]; // }; void handleReserved(DataInput data) throws IOException { byte pad1 = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); byte info[] = new byte[16]; data.readFully(info); } // struct udp_reply_spacket { /* UDP */ // char type; /* SP_UDP_REPLY */ // char reply; // char pad1; // char pad2; // int port; // }; void handleUDPReply(DataInput data) throws IOException { byte reply = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); int port = data.readInt(); } // struct sequence_spacket { /* UDP */ // char type; /* SP_SEQUENCE */ // char pad1; // unsigned short sequence; // }; void handleSequence(DataInput data) throws IOException { data.readFully(buf, 1, 3); /* byte pad1 = data.readByte(); short sequence = data.readShort(); */ } // struct rsa_key_spacket { // char type; /* SP_RSA_KEY */ // char pad1; // char pad2; // char pad3; // unsigned char data[KEY_SIZE]; //}; void handleRSAKey(DataInput data) throws IOException { byte pad1 = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); byte key[] = new byte[32]; data.readFully(key); } // struct shortreply_spacket { /* SP_S_REPLY */ // char type; // char repl; // unsigned short winside; // LONG gwidth; // }; void handleSReply(DataInput data) throws IOException { byte repl = data.readByte(); short winside = data.readShort(); int gwidth = data.readInt(); } // struct ping_spacket { // char type; /* SP_PING */ // unsigned char number; /* id (ok to wrap) */ // unsigned short lag; /* delay of last ping in ms */ // unsigned char tloss_sc; /* total loss server-client 0-100% */ // unsigned char tloss_cs; /* total loss client-server 0-100% */ // unsigned char iloss_sc; /* inc. loss server-client 0-100% */ // unsigned char iloss_cs; /* inc. loss client-server 0-100% */ // }; void handlePing(DataInput data) throws IOException { data.readFully(buf, 1, 7); /* byte number = data.readByte(); short lag = data.readShort(); byte tloss_sc = data. readByte(); byte tloss_cs = data. readByte(); byte iloss_sc = data. readByte(); byte iloss_cs = data. readByte(); */ } // struct feature_cpacket { // char type; // char feature_type; // char arg1; // char arg2; // int value; // char name[80]; // }; void handleFeature(DataInput data) throws IOException { byte ftype = data.readByte(); byte arg1 = data.readByte(); byte arg2 = data.readByte(); int value = data.readInt(); data.readFully(buf, 0, 80); DataOutputStream dout = new DataOutputStream(features); try { dout.writeByte(SP_FEATURE); dout.writeByte(ftype); dout.writeByte(arg1); dout.writeByte(arg2); dout.writeInt(value); dout.write(buf, 0, 80); } catch (IOException ex) { } } void handleSync(DataInput data) throws IOException { data.readFully(buf, 1, 3); byte tract = buf[1]; byte lock = buf[2]; if (tract < 0 || tract >= players.Number()) state.me.p_tractor = null; else state.me.p_tractor = players.GetPlayer(tract); state.ready = true; ready = true; } public void DumpStateInit(DataOutput data) throws IOException { players.DumpStateInit(data); state.DumpStateInit(data); data.write(features.toByteArray()); planets.DumpStateInit(data); phasers.DumpStateInit(data); torps.DumpStateInit(data); plasmas.DumpStateInit(data); sendLogin(data); sendPickOK(data); } public void DumpState(DataOutput data) throws IOException { state.DumpState(data); players.DumpState(data); planets.DumpState(data); phasers.DumpState(data); torps.DumpState(data); plasmas.DumpState(data); } } class NetrekGame extends java.awt.Panel { final int SZ_BORDER = 3; final int SZ_MESSAGE = 20; final int SZ_DASHBOARD = SZ_MESSAGE + SZ_BORDER + SZ_MESSAGE; public final static int VIEWALL = 0; public final static int VIEWGALACTIC = 1; public final static int VIEWLOCAL = 2; NetrekState game_state; int view_mode; Dimension gsize; LocalMap local; GalacticMap galactic; DashBoard dash; NetrekImageLib img_lib = null; boolean layed_out = false; public NetrekGame() { this(null); } public NetrekGame(NetrekImageLib lib) { view_mode = VIEWALL; setLayout(null); setForeground(Color.white); setBackground(Color.black); Toolkit tk = Toolkit.getDefaultToolkit(); Font fn; FontMetrics fm; int size = 10; do { fn = new Font("Courier", Font.PLAIN, size); fm = tk.getFontMetrics(fn); size--; } while (fm.getMaxAdvance() > 6); setFont(fn); restart(); SetImageLib(lib); } public NetrekState GetGameState() { return game_state.copy(); } public void SetGameState(NetrekState state) { game_state = state; local.SetGameState(state); galactic.SetGameState(state); dash.SetGameState(state); } public void SetImageLib(NetrekImageLib lib) { img_lib = lib; local.SetImageLib(lib); galactic.SetImageLib(lib); } public void SetObserver(boolean obs) { game_state.state.observer = obs; } public void SetViewMode(int mode) { view_mode = mode; layed_out = false; UpdateSize(); } public void restart() { layed_out = false; removeAll(); game_state = new NetrekState(); local = new LocalMap(game_state, img_lib); local.setFont(getFont()); local.setBackground(getBackground()); add(local); galactic = new GalacticMap(game_state, img_lib); galactic.setFont(getFont()); galactic.setBackground(getBackground()); add(galactic); dash = new DashBoard(game_state); dash.setFont(getFont()); dash.setBackground(getBackground()); add(dash); UpdateSize(); } public void UpdateSize() { int winside = game_state.state.winside; if (view_mode == VIEWLOCAL) gsize = new Dimension(winside + SZ_BORDER, winside + SZ_BORDER + SZ_DASHBOARD); else if (view_mode == VIEWGALACTIC) gsize = new Dimension(winside, winside); else gsize = new Dimension(winside + SZ_BORDER + winside, winside + SZ_BORDER + SZ_DASHBOARD); resize(gsize.width, gsize.height); } public synchronized void mylayout() { Insets ins = insets(); int winside = game_state.state.winside; if (view_mode == VIEWLOCAL) { local.reshape(ins.left, ins.top, winside, winside); dash.reshape(ins.left, ins.top + winside + SZ_BORDER, winside, SZ_DASHBOARD); galactic.hide(); } else if (view_mode == VIEWGALACTIC) { galactic.reshape(ins.left, ins.top, winside, winside); local.hide(); dash.hide(); } else { local.reshape(ins.left, ins.top, winside, winside); galactic.reshape(ins.left + winside+SZ_BORDER, ins.top, winside, winside); dash.reshape(ins.left, ins.top + winside + SZ_BORDER, winside, SZ_DASHBOARD); } } public Dimension Size() { return gsize; } public Dimension minimumSize() { return gsize; } public Dimension preferredSize() { return gsize; } public boolean IsReady() { return game_state.ready; } public void Refresh() { local.Refresh(); galactic.Refresh(); dash.Refresh(); } public void DrawState() { Graphics g; if (!layed_out) { mylayout(); layed_out = true; } synchronized (game_state) { local.paint(); galactic.paint(); dash.paint(); g = getGraphics(); if (g == null) return; paint(g); } } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { if (!isEnabled()) return; if (img_lib == null) return; paintBorders(g); super.paint(g); } public void paintBorders(Graphics g) { Color color; int side = game_state.state.winside; if (g == null) return; if (!game_state.state.ready) return; switch (game_state.state.me.GetAlert()) { case Player.PFRED: color = Color.red; break; case Player.PFYELLOW: color = Color.yellow; break; case Player.PFGREEN: color = Color.green; break; default: color = Color.white; } g.setColor(color); g.fillRect(0, side, side + SZ_BORDER, SZ_BORDER); g.fillRect(side, 0, SZ_BORDER, side + SZ_BORDER); g.setColor(getForeground()); g.fillRect(side + SZ_BORDER, side, side, SZ_BORDER); g.fillRect(side, side + SZ_BORDER, SZ_BORDER, SZ_DASHBOARD); } public boolean step(DataInput data) { synchronized (game_state) return game_state.step(data); } public void DumpStateInit(DataOutput data) throws IOException { synchronized (game_state) game_state.DumpStateInit(data); } public void DumpState(DataOutput data) throws IOException { synchronized (game_state) game_state.DumpState(data); } } class ServerState implements Cloneable { final int SPWINSIDE = 500; final int SPGWIDTH = 100000; public final int SERVER_SCALE = 40; public boolean observer; public boolean ready = false; public int gwidth; public int winside; public int view; public int server_scale; public byte s_tourn; // This has to go. Some time when I have the mood int tournMask; int s_armsbomb; int s_planets; int s_kills; int s_losses; int s_time; int s_timeprod; public Player me; public ServerState() { observer = false; gwidth = SPGWIDTH; winside = SPWINSIDE; server_scale = SERVER_SCALE; tournMask = 0x0F; me = null; view = server_scale * winside / 2; } public ServerState copy() { try { return (ServerState) clone(); } catch(CloneNotSupportedException ex) { return null; } } public void SetMask(int mask) { tournMask = mask; } public int RelativeToRealXCoordinate(int coord) { if (me == null || coord > winside) return -10000; return me.p_x + (coord - winside/2) * server_scale; } public int RelativeToRealYCoordinate(int coord) { if (me == null || coord > winside) return -10000; return me.p_y + (coord - winside/2) * server_scale; } public int GalacticToRealCoordinate(int coord) { return coord * (gwidth / winside); } public int RealToGalacticCoordinate(int coord) { return (coord * winside) / gwidth; } public int RealToRelativeXCoordinate(int coord) { int dc = coord - me.p_x; return dc / server_scale + winside / 2; } public int RealToRelativeYCoordinate(int coord) { int dc = coord - me.p_y; return dc / server_scale + winside / 2; } public boolean IsVisible(int x, int y) { return !(x < 0 || x > winside || y < 0 || y > winside); } // struct mask_spacket { // char type; /* SP_MASK */ // char mask; // char pad1; // char pad2; // }; void handleMask(DataInput data, ServerState state) throws IOException { byte mask = data.readByte(); byte pad2 = data.readByte(); byte pad3 = data.readByte(); tournMask = mask; } public void sendMask(DataOutput data) throws IOException { data.writeByte(NetrekState.SP_MASK); data.writeByte(tournMask); data.writeByte(0); data.writeByte(0); } // struct status_spacket { // char type; /* SP_STATUS */ // char tourn; // char pad1; // char pad2; // unsigned armsbomb; // unsigned planets; // unsigned kills; // unsigned losses; // unsigned time; // unsigned LONG timeprod; // }; public void handleStatus(DataInput data) throws IOException { byte tourn = data.readByte(); byte pad1 = data.readByte(); byte pad2 = data.readByte(); int armsbomb = data.readInt(); int planets = data.readInt(); int kills = data.readInt(); int losses = data.readInt(); int time = data.readInt(); int timeprod = data.readInt(); s_tourn = tourn; s_armsbomb = armsbomb; s_planets = planets; s_kills = kills; s_losses = losses; s_time = time; s_timeprod = timeprod; } public void sendStatus(DataOutput data) throws IOException { data.writeByte(NetrekState.SP_STATUS); data.writeByte(s_tourn); data.writeByte(0); data.writeByte(0); data.writeInt(s_armsbomb); data.writeInt(s_planets); data.writeInt(s_kills); data.writeInt(s_losses); data.writeInt(s_time); data.writeInt(s_timeprod); } public void DumpStateInit(DataOutput data) throws IOException { } public void DumpState(DataOutput data) throws IOException { sendStatus(data); sendMask(data); } } class Eraser { int areanum; Rectangle areas[]; int linenum; Point lines[]; public Eraser(int maxareas, int maxlines) { areanum = 0; areas = new Rectangle[maxareas]; for (int i = 0; i < maxareas; i++) areas[i] = new Rectangle(); linenum = 0; lines = new Point[maxlines*2]; for (int i = 0; i < maxlines*2; i++) lines[i] = new Point(0, 0); } public boolean AddArea(int x, int y, int w, int h) { if (areanum >= areas.length) return false; areas[areanum].x = x; areas[areanum].y = y; areas[areanum].width = w; areas[areanum].height = h; areanum++; return true; } public boolean AddLine(int x1, int y1, int x2, int y2) { if (linenum >= lines.length) return false; int ln2 = linenum*2; lines[ln2].x = x1; lines[ln2].y = y1; lines[ln2+1].x = x2; lines[ln2+1].y = y2; linenum++; return true; } public int Areas() { return areanum; } public int Lines() { return linenum; } public void Erase(Graphics g, Color bgcolor) { for (int i = 0; i < areanum; i++) g.clearRect(areas[i].x, areas[i].y, areas[i].width, areas[i].height); if (linenum > 0) { g.setColor(bgcolor); for (int i = 0; i < linenum*2; i += 2) g.drawLine(lines[i].x, lines[i].y, lines[i+1].x, lines[i+1].y); } areanum = linenum = 0; } public void Forget() { areanum = linenum = 0; } }