summaryrefslogtreecommitdiff
path: root/java/pclient.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/pclient.java')
-rw-r--r--java/pclient.java1196
1 files changed, 1196 insertions, 0 deletions
diff --git a/java/pclient.java b/java/pclient.java
new file mode 100644
index 0000000..7e75536
--- /dev/null
+++ b/java/pclient.java
@@ -0,0 +1,1196 @@
+import java.applet.*;
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.zip.*;
+import java.lang.*;
+
+
+public class pclient extends Applet {
+
+ // Utilities
+
+ public static String[] splitString(String s, char delim) {
+ // StringTokenizer drops empty tokens :-(
+ int count = 1;
+ int length = s.length();
+ for (int i=0; i<length; i++) {
+ if (s.charAt(i) == delim)
+ count++;
+ }
+ String[] result = new String[count];
+ int origin = 0;
+ count = 0;
+ for (int i=0; i<length; i++) {
+ if (s.charAt(i) == delim) {
+ result[count++] = s.substring(origin, i);
+ origin = i+1;
+ }
+ }
+ result[count] = s.substring(origin);
+ return result;
+ }
+
+ public static String readLine(InputStream st) throws IOException {
+ String result = "";
+ int c;
+ while ((c = st.read()) != (byte) '\n') {
+ if (c < 0)
+ throw new IOException("unexpected end of stream");
+ result += (char) c;
+ }
+ return result.trim();
+ }
+
+ public static void readAll(InputStream st, byte[] buffer, int off, int len)
+ throws IOException {
+ while (len > 0) {
+ int count = st.read(buffer, off, len);
+ if (count <= 0)
+ throw new IOException("unexpected end of data");
+ off += count;
+ len -= count;
+ }
+ }
+
+ public static Color makeColor(int color) {
+ return new Color(color & 0xFF,
+ (color >> 8) & 0xFF,
+ (color >> 16) & 0xFF);
+ }
+
+ public static InputStream decompresser(byte[] data, int off, int len) {
+ return new InflaterInputStream(new ByteArrayInputStream(data, off, len));
+ }
+
+ /*class ArraySlice {
+ public byte[] array;
+ public int ofs, size;
+ ArraySlice(byte[] aarray, int aofs, int asize) {
+ array = aarray;
+ ofs = aofs;
+ size = asize;
+ }
+ }*/
+
+ public void debug(Throwable e) {
+ showStatus(e.toString());
+ e.printStackTrace();
+ }
+
+ // Bitmaps and icons
+
+ class Bitmap {
+
+ int w, h;
+ public int[] pixelData;
+
+ Bitmap(pclient client, InputStream st, int keycol) throws IOException {
+ String line = readLine(st);
+ if (!"P6".equals(line)) throw new IOException("not a P6 PPM image");
+ while ((line = readLine(st)).startsWith("#"))
+ ;
+ String[] wh = splitString(line, ' ');
+ if (wh.length != 2) throw new IOException("invalid PPM image size");
+ w = Integer.parseInt(wh[0]);
+ h = Integer.parseInt(wh[1]);
+ line = readLine(st);
+ if (!"255".equals(line))
+ throw new IOException("not a 255-levels PPM image");
+
+ // over-allocate an extra uninitialized line at the bottom of the
+ // image to work around a bug in the MemoryImageSource constructor
+ pixelData = new int[w*(h+1)];
+ int target = 0;
+ int w3 = 3*w;
+ byte[] lineBuffer = new byte[w3];
+ for (int y=0; y<h; y++) {
+ readAll(st, lineBuffer, 0, w3);
+ for (int x3=0; x3<w3; x3+=3) {
+ int rgb = (((((int) lineBuffer[x3] ) & 0xFF) << 16) |
+ ((((int) lineBuffer[x3+1]) & 0xFF) << 8 ) |
+ ((((int) lineBuffer[x3+2]) & 0xFF) ));
+ if (rgb == keycol)
+ rgb = 0;
+ else
+ rgb |= 0xFF << 24;
+ pixelData[target++] = rgb;
+ }
+ }
+ }
+
+ public Image extractIcon(int x1, int y1, int w1, int h1) {
+ return createImage(new MemoryImageSource(w1, h1, pixelData,
+ x1 + y1*w, w));
+ }
+ }
+
+ // Host choosing
+
+ public static final int defaultPort = 8056;
+ public static final String pingMessage = "pclient-game-ping";
+ public static final String pongMessage = "server-game-pong";
+
+ public Socket pickHost(String udphostname, int port)
+ throws IOException {
+ InetAddress addr = InetAddress.getByName(udphostname);
+ byte[] msg = pingMessage.getBytes("UTF8");
+ DatagramPacket outp = new DatagramPacket(msg, msg.length, addr, port);
+ byte[] buffer = new byte[200];
+ DatagramPacket inp = new DatagramPacket(buffer, buffer.length);
+ DatagramSocket s = new DatagramSocket();
+ showStatus("Looking for a game server on "+udphostname+":"+
+ Integer.toString(port)+"...");
+ s.send(outp);
+
+ s.receive(inp);
+ String inpmsg = new String(inp.getData(), 0, inp.getLength(), "UTF8");
+ String[] data = splitString(inpmsg, ':');
+ //System.out.println(inpmsg);
+ //System.out.println(data.length);
+ //System.out.println("<<<"+data[0]+">>>");
+ if (data.length >= 4 && pongMessage.equals(data[0])) {
+ InetAddress result;
+ if (data[2].length() == 0) {
+ result = inp.getAddress();
+ }
+ else {
+ result = InetAddress.getByName(data[2]);
+ }
+ port = Integer.parseInt(data[3]);
+ showStatus("Connecting to "+data[1]+" at "+
+ result.toString()+":"+Integer.toString(port)+"...");
+ return new Socket(result, port);
+ }
+ else
+ throw new IOException("got an unexpected answer from " +
+ inp.getAddress().toString());
+ }
+
+ // Game state
+
+ class Player {
+ public int pid;
+ public boolean playing;
+ public boolean local;
+ public Image icon;
+ public int xmin, xmax;
+ }
+
+ class KeyName {
+ public String keyname;
+ public int keyid;
+ public Image[] keyicons;
+ public KeyName next;
+ public int newkeycode;
+ }
+
+ public Player[] players = new Player[0];
+ public KeyName keys = null;
+ public Hashtable keycodes = new Hashtable();
+ public boolean taskbarfree = false;
+ public boolean taskbarmode = false;
+ public KeyName keydefinition_k = null;
+ public int keydefinition_pid;
+ public Image[] iconImages = new Image[0];
+ public Bitmap[] bitmaps = new Bitmap[0];
+
+ public Player getPlayer(int id) {
+ if (id >= players.length) {
+ Player[] newply = new Player[id+1];
+ System.arraycopy(players, 0, newply, 0, players.length);
+ players = newply;
+ }
+ if (players[id] == null) {
+ players[id] = new Player();
+ players[id].pid = id;
+ players[id].playing = false;
+ players[id].local = false;
+ }
+ return players[id];
+ }
+
+ public Player nextPlayer(Player prev) {
+ int i;
+ for (i=prev.pid+1; i<players.length; i++)
+ if (players[i] != null)
+ return players[i];
+ return null;
+ }
+
+ public Player firstPlayer() {
+ int i;
+ for (i=0; i<players.length; i++)
+ if (players[i] != null)
+ return players[i];
+ return null;
+ }
+
+ public void setTaskbar(boolean nmode) {
+ if (taskbarfree) {
+ Player p;
+ boolean nolocalplayer = true;
+ for (p=firstPlayer(); p!=null; p=nextPlayer(p))
+ if (p.local)
+ nolocalplayer = false;
+ boolean prevmode = taskbarmode;
+ taskbarmode = nmode || nolocalplayer || keydefinition_k != null;
+ if (prevmode != taskbarmode)
+ repaint();
+ }
+ }
+
+ public final Image getIcon(int ico) {
+ if (ico < 0 || ico >= iconImages.length)
+ return null;
+ else
+ return iconImages[ico];
+ }
+
+ public void setIcon(int ico, Image img) {
+ if (ico >= iconImages.length) {
+ Image[] newico = new Image[ico+1];
+ System.arraycopy(iconImages, 0, newico, 0, iconImages.length);
+ iconImages = newico;
+ }
+ iconImages[ico] = img;
+ }
+
+ public void setBitmap(int n, Bitmap bmp) {
+ if (n >= bitmaps.length) {
+ Bitmap[] newbmp = new Bitmap[n+1];
+ System.arraycopy(bitmaps, 0, newbmp, 0, bitmaps.length);
+ bitmaps = newbmp;
+ }
+ bitmaps[n] = bmp;
+ }
+
+ // Sprites
+
+ class Sprite {
+ public int x, y, ico;
+ public Image bkgnd;
+
+ public final boolean draw(pclient client, Image backBuffer,
+ Graphics backGC) {
+ Image iconImage = client.getIcon(ico);
+ if (iconImage == null) {
+ ico = -1;
+ return false;
+ }
+ int w = iconImage.getWidth(client);
+ int h = iconImage.getHeight(client);
+
+ if (bkgnd == null || bkgnd.getWidth(client) != w ||
+ bkgnd.getHeight(client) != h) {
+ bkgnd = client.createImage(w, h);
+ }
+ bkgnd.getGraphics().drawImage(backBuffer, -x, -y, client);
+ backGC.drawImage(iconImage, x, y, client);
+ //System.out.println("Draw at "+Integer.toString(x)+", "+
+ // Integer.toString(y));
+ return true;
+ }
+
+ public final void erase(pclient client, Graphics backGC) {
+ if (ico != -1) {
+ //System.out.println("Erase at "+Integer.toString(x)+", "+
+ // Integer.toString(y));
+ backGC.drawImage(bkgnd, x, y, client);
+ }
+ }
+ }
+
+ // Playfield
+
+ class Playfield {
+ public static final int TASKBAR_HEIGHT = 48;
+
+ public pclient client;
+ public int pfwidth, pfheight;
+ public Image backBuffer;
+ public Sprite[] sprites = new Sprite[0];
+ public int numSprites = 0;
+ public byte[] pendingBuffer = new byte[SocketDisplayer.UDP_BUF_SIZE];
+ public int pendingBufOfs = 0;
+ public int pendingBufLen = 0;
+ public byte[] spriteData = new byte[0];
+ public int validDataLen = 0;
+ public Image tbCache;
+ public AudioClip[] samples = new AudioClip[0];
+ public int[] playingSounds = new int[0];
+
+ Playfield(pclient aclient, int width, int height, Color bkgnd) {
+ client = aclient;
+ pfwidth = width;
+ pfheight = height;
+ backBuffer = createImage(width, height);
+ Graphics backGC = backBuffer.getGraphics();
+ backGC.setColor(bkgnd);
+ backGC.fillRect(0, 0, width, height);
+ backGC.dispose();
+ client.resize(width, height);
+ client.setBackground(bkgnd);
+
+ int[] pixelData = new int[32*TASKBAR_HEIGHT];
+ int target = 0;
+ for (int y=0; y<TASKBAR_HEIGHT; y++) {
+ int alpha = y * 256 / TASKBAR_HEIGHT;
+ int rgb = 0x8080FF | (alpha<<24);
+ for (int x=0; x<32; x++)
+ pixelData[target++] = rgb;
+ }
+ tbCache = createImage(new MemoryImageSource(32, TASKBAR_HEIGHT,
+ pixelData, 0, 32));
+ }
+
+ public synchronized byte[] setSprites(byte[] buf, int buflen) {
+ byte[] old = pendingBuffer;
+ pendingBuffer = buf;
+ //System.out.println("UDP packet for "+Integer.toString(buflen/6));
+
+ /* sound support -- no volume */
+ int base = 0;
+ int[] currentSounds = new int[samples.length];
+ while (base+6 <= buflen &&
+ pendingBuffer[base+4] == -1 &&
+ pendingBuffer[base+5] == -1) {
+ int key = pendingBuffer[base+1];
+ key = (key & 0xFF) | (((int) pendingBuffer[base]) << 8);
+ if (0 <= key && key <= 9999) { /* safety bound check */
+ if (key >= samples.length) {
+ AudioClip[] newclip = new AudioClip[key+5];
+ System.arraycopy(samples, 0, newclip, 0, samples.length);
+ samples = newclip;
+ }
+ if (samples[key] == null) {
+ String filename = "sample.wav?code=" +
+ Integer.toString(key);
+ samples[key] = getAudioClip(getCodeBase(), filename);
+ }
+ else if (playingSounds.length > key &&
+ playingSounds[key] > 0) {
+ currentSounds[key] = playingSounds[key] - 1;
+ }
+ else {
+ samples[key].play();
+ currentSounds[key] = 4;
+ }
+ }
+ base += 6;
+ }
+ playingSounds = currentSounds;
+ pendingBufOfs = base;
+ pendingBufLen = buflen - base;
+ return old;
+ }
+
+ public synchronized int fetchSprites() {
+ int valid;
+ int count = (pendingBufLen < validDataLen) ? pendingBufLen
+ : validDataLen;
+
+ for (valid=0; valid<count; valid++)
+ if (spriteData[valid] != pendingBuffer[pendingBufOfs+valid])
+ break;
+ validDataLen = valid;
+
+ if (pendingBufLen > spriteData.length) {
+ spriteData = new byte[pendingBufLen+90];
+ valid = 0;
+ }
+ System.arraycopy(pendingBuffer, pendingBufOfs+valid,
+ spriteData, valid, pendingBufLen-valid);
+ return pendingBufLen;
+ }
+
+ public void paint(Graphics g) {
+ int buflen = fetchSprites();
+ byte[] buffer = spriteData;
+ int count = validDataLen / 6;
+ int base = count * 6;
+ int nspr = buflen / 6;
+ Image tback;
+
+ if (nspr > sprites.length) {
+ Sprite[] newspr = new Sprite[nspr+15];
+ System.arraycopy(sprites, 0, newspr, 0, sprites.length);
+ for (int i=sprites.length; i<newspr.length; i++)
+ newspr[i] = new Sprite();
+ sprites = newspr;
+ }
+
+ //System.out.println("drawImage: -"+
+ // Integer.toString(numSprites-count)+
+ // " +"+Integer.toString(nspr-count));
+
+ // erase extra sprites
+ Graphics backGC = backBuffer.getGraphics();
+ for (int i=numSprites-1; i>=count; i--) {
+ sprites[i].erase(client, backGC);
+ }
+
+ // draw new sprites
+ validDataLen = pendingBufLen;
+ while (count < nspr) {
+ Sprite s = sprites[count++];
+ int x = buffer[base+1];
+ s.x = (x & 0xFF) | (((int) buffer[base ]) << 8);
+ int y = buffer[base+3];
+ s.y = (y & 0xFF) | (((int) buffer[base+2]) << 8);
+ int ico = buffer[base+5];
+ s.ico = (ico & 0xFF) | (((int) buffer[base+4]) << 8);
+ if (!s.draw(client, backBuffer, backGC)) {
+ if (base < validDataLen)
+ validDataLen = base;
+ //System.out.println(Integer.toString(s.x)+';'+
+ // Integer.toString(s.y)+';'+
+ // Integer.toString(s.ico));
+ }
+ base += 6;
+ }
+ numSprites = count;
+
+ if (client.taskbarmode)
+ tback = paintTaskbar(backGC);
+ else
+ tback = null;
+
+ g.drawImage(backBuffer, 0, 0, client);
+
+ if (tback != null)
+ eraseTaskbar(backGC, tback);
+ backGC.dispose();
+ }
+
+ public Image paintTaskbar(Graphics g) {
+ boolean animated = false;
+ int y0 = pfheight - TASKBAR_HEIGHT;
+ Image bkgnd = client.createImage(pfwidth, TASKBAR_HEIGHT);
+ bkgnd.getGraphics().drawImage(backBuffer, 0, -y0, client);
+ for (int i=0; i<pfwidth; i+=32)
+ g.drawImage(tbCache, i, y0, client);
+
+ double f = 0.0015 * new Date().getTime();
+ f = f - (int) f;
+ double f2 = f * (1.0-f) * 4.0;
+
+ int lpos = 0;
+ int rpos = pfwidth;
+ for (Player p=firstPlayer(); p!=null; p=nextPlayer(p))
+ if (p.icon != null) {
+ Image ico = p.icon;
+ int x, y;
+ int w = ico.getWidth(client);
+ int h = ico.getHeight(client);
+ int dx = w * 5 / 3;
+ p.xmin = p.xmax = 0;
+
+ if (p.local) {
+ rpos -= dx;
+ int dy = TASKBAR_HEIGHT - h - 1;
+ x = rpos;
+ y = pfheight - h - (int)(dy*f2);
+ animated = true;
+ }
+ else {
+ lpos += dx;
+ if (p.playing)
+ continue;
+ x = lpos - w;
+ y = pfheight - h;
+ if (keydefinition_k != null &&
+ keydefinition_pid == p.pid) {
+ Image[] icons = keydefinition_k.keyicons;
+ if (icons.length > 0) {
+ int index = (int)(f*icons.length);
+ ico = icons[index % icons.length];
+ animated = true;
+ }
+ y = y0 + (TASKBAR_HEIGHT-h)/2;
+ }
+ }
+ p.xmin = x;
+ p.xmax = x + w;
+ g.drawImage(ico, x, y, client);
+ }
+
+ if (animated)
+ repaint(50);
+ return bkgnd;
+ }
+
+ public void eraseTaskbar(Graphics g, Image bkgnd) {
+ int y0 = pfheight - TASKBAR_HEIGHT;
+ g.drawImage(bkgnd, 0, y0, client);
+ }
+ }
+
+ // Socket listener
+
+ class SocketListener extends Thread {
+
+ public pclient client;
+ public Socket socket;
+ public InputStream socketInput;
+ public OutputStream socketOutput;
+
+ public static final String MSG_WELCOME = "Welcome to gamesrv.py(3) !\n";
+ public static final byte MSG_DEF_PLAYFIELD = (byte) 'p';
+ public static final byte MSG_DEF_KEY = (byte) 'k';
+ public static final byte MSG_DEF_ICON = (byte) 'r';
+ public static final byte MSG_DEF_BITMAP = (byte) 'm';
+ public static final byte MSG_DEF_SAMPLE = (byte) 'w';
+ public static final byte MSG_DEF_MUSIC = (byte) 'z';
+ public static final byte MSG_PLAY_MUSIC = (byte) 'Z';
+ public static final byte MSG_FADEOUT = (byte) 'f';
+ public static final byte MSG_PLAYER_JOIN = (byte) '+';
+ public static final byte MSG_PLAYER_KILL = (byte) '-';
+ public static final byte MSG_PLAYER_ICON = (byte) 'i';
+ public static final byte MSG_PING = (byte) 'g';
+ public static final byte MSG_PONG = (byte) 'G';
+ public static final byte MSG_INLINE_FRAME = (byte) '\\';
+
+ public static final byte CMSG_KEY = (byte) 'k';
+ public static final byte CMSG_ADD_PLAYER = (byte) '+';
+ public static final byte CMSG_REMOVE_PLAYER= (byte) '-';
+ public static final byte CMSG_UDP_PORT = (byte) '<';
+ public static final byte CMSG_ENABLE_SOUND = (byte) 's';
+ public static final byte CMSG_ENABLE_MUSIC = (byte) 'm';
+ public static final byte CMSG_PING = (byte) 'g';
+ public static final byte CMSG_PONG = (byte) 'G';
+ public static final byte CMSG_PLAYER_NAME = (byte) 'n';
+
+ public void connectionClosed() throws IOException {
+ throw new IOException("connection closed");
+ }
+
+ public void protocolError() throws IOException {
+ throw new IOException("protocol error");
+ }
+
+ SocketListener(pclient aclient, Socket asocket) throws IOException {
+ setDaemon(true);
+ byte[] msgWelcome = MSG_WELCOME.getBytes("UTF8");
+
+ client = aclient;
+ socket = asocket;
+ socketInput = socket.getInputStream();
+ socketOutput = socket.getOutputStream();
+
+ for (int i=0; i<msgWelcome.length; i++) {
+ int recv = socketInput.read();
+ if (recv != msgWelcome[i])
+ throw new IOException
+ ("connected to something not a game server");
+ }
+ showStatus("Let's "+client.readLine(socketInput)+"!");
+ }
+
+ public void sendData(byte[] buffer, int ofs, int size)
+ throws IOException {
+ socketOutput.write(buffer, ofs, size);
+ }
+
+ public byte[] codeMessage(int p, byte msgcode, int[] args,
+ String lastarg) {
+ int bufsize = 1;
+ String mode = "";
+ for (int i=0; i<args.length; i++) {
+ mode = mode + "l";
+ bufsize = bufsize + 4;
+ }
+ if (lastarg != null) {
+ mode = mode + Integer.toString(lastarg.length()) + "s";
+ bufsize = bufsize + lastarg.length();
+ }
+ byte[] buffer = new byte[p + 1 + mode.length() + bufsize];
+ buffer[p++] = (byte) mode.length();
+ for (int i=0; i<mode.length(); i++) {
+ buffer[p++] = (byte) mode.charAt(i);
+ }
+ buffer[p++] = msgcode;
+ for (int i=0; i<args.length; i++) {
+ int n;
+ int value = args[i];
+ buffer[p++] = (byte) (value >> 24);
+ n = value >> 16;
+ buffer[p++] = (byte)(((n&0x80) == 0) ? n&0x7F : n|0xFFFFFF80);
+ n = value >> 8;
+ buffer[p++] = (byte)(((n&0x80) == 0) ? n&0x7F : n|0xFFFFFF80);
+ n = value;
+ buffer[p++] = (byte)(((n&0x80) == 0) ? n&0x7F : n|0xFFFFFF80);
+ }
+ if (lastarg != null) {
+ for (int i=0; i<lastarg.length(); i++) {
+ buffer[p++] = (byte) lastarg.charAt(i);
+ }
+ }
+ return buffer;
+ }
+
+ public void sendMessage(byte msgcode, int[] args) throws IOException {
+ sendMessageEx(msgcode, args, null);
+ }
+
+ public void sendMessageEx(byte msgcode, int[] args, String lastarg)
+ throws IOException {
+ byte[] buffer = codeMessage(0, msgcode, args, lastarg);
+ sendData(buffer, 0, buffer.length);
+ }
+
+ public int decodeMessage(byte[] buffer, int ofs, int end)
+ throws IOException {
+ if (ofs == end) return -1;
+ int typecodes = buffer[ofs]; typecodes &= 0xFF;
+ int base = ofs+1+typecodes;
+ if (base >= end) return -1;
+ byte msgcode = buffer[base++];
+ int[] args = new int[typecodes];
+ int repeatcount = 0;
+ int nargs = 0;
+ for (int i=0; i<typecodes; i++) {
+ byte c = buffer[ofs+1+i];
+ if (c == (byte) 'B') {
+ if (base+1 > end)
+ return -1;
+ args[nargs++] = ((int) buffer[base++]) & 0xFF;
+ }
+ else if (c == (byte) 'l') {
+ if (base+4 > end)
+ return -1;
+ int n4 = buffer[base++];
+ int n3 = buffer[base++]; n3 &= 0xFF;
+ int n2 = buffer[base++]; n2 &= 0xFF;
+ int n1 = buffer[base++]; n1 &= 0xFF;
+ int value = n1 | (n2<<8) | (n3<<16) | (n4<<24);
+ //System.out.println(n4);
+ //System.out.println(n3);
+ //System.out.println(n2);
+ //System.out.println(n1);
+ //System.out.println(value);
+ //System.out.println();
+ args[nargs++] = value;
+ }
+ else if ((byte) '0' <= c && c <= (byte) '9') {
+ repeatcount = repeatcount*10 + (c - (byte) '0');
+ }
+ else if (c == (byte) 's') {
+ if (base+repeatcount > end)
+ return -1;
+ args[nargs++] = base;
+ args[nargs++] = repeatcount;
+ base += repeatcount;
+ repeatcount = 0;
+ }
+ else
+ protocolError();
+ }
+
+ //System.out.print("Message ");
+ //System.out.print((char) msgcode);
+ //for (int i=0; i<nargs; i++)
+ // System.out.print(" " + Integer.toString(args[i]));
+ //System.out.println();
+
+ switch (msgcode) {
+
+ case MSG_PLAYER_JOIN: {
+ int id = args[0];
+ int local = args[1];
+ Player p = client.getPlayer(id);
+ p.playing = true;
+ p.local = local != 0;
+ if (p.local)
+ client.setTaskbar(false);
+ break;
+ }
+ case MSG_PLAYER_KILL: {
+ int id = args[0];
+ Hashtable nkeycodes = new Hashtable();
+ Player p = client.getPlayer(id);
+ p.playing = false;
+ p.local = false;
+ for (Enumeration e = client.keycodes.keys();
+ e.hasMoreElements(); ) {
+ Object key = e.nextElement();
+ byte[] msg = (byte[]) keycodes.get(key);
+ if (msg[0] != id)
+ nkeycodes.put(key, msg);
+ }
+ client.keycodes = nkeycodes;
+ break;
+ }
+ case MSG_DEF_PLAYFIELD: {
+ int width = args[0];
+ int height = args[1];
+ Color bkgnd = client.makeColor(args[2]);
+ client.playfield = new pclient.Playfield(client, width, height,
+ bkgnd);
+ int[] singleint = new int[1];
+ singleint[0] = -1;
+ sendMessage(CMSG_ENABLE_SOUND, singleint);
+ sendMessage(CMSG_PING, new int[0]);
+ break;
+ }
+ case MSG_DEF_KEY: {
+ int i;
+ int nameofs = args[0];
+ int namelen = args[1];
+ int keyid = args[2];
+ int nicons = nargs - 3;
+ KeyName key = new KeyName();
+ key.keyname = new String(buffer, nameofs, namelen, "UTF8");
+ key.keyid = keyid;
+ key.keyicons= new Image[nicons];
+ for (i=0; i<nicons; i++)
+ key.keyicons[i] = client.getIcon(args[3+i]);
+ if (client.keys == null || keyid < client.keys.keyid) {
+ key.next = client.keys;
+ client.keys = key;
+ }
+ else {
+ KeyName k = client.keys;
+ while (k.next != null && k.next.keyid < keyid)
+ k = k.next;
+ key.next = k.next;
+ k.next = key;
+ }
+ break;
+ }
+ case MSG_DEF_ICON: {
+ int bmpcode = args[0];
+ int icocode = args[1];
+ int ix = args[2];
+ int iy = args[3];
+ int iw = args[4];
+ int ih = args[5];
+ if (bmpcode < client.bitmaps.length) {
+ Bitmap bmp = client.bitmaps[bmpcode];
+ if (bmp != null)
+ client.setIcon(icocode,
+ bmp.extractIcon(ix, iy, iw, ih));
+ }
+ break;
+ }
+ case MSG_DEF_BITMAP: {
+ int bmpcode = args[0];
+ int dataofs = args[1];
+ int datalen = args[2];
+ int colorkey = (nargs > 3) ? args[3] : -1;
+ InputStream st = decompresser(buffer, dataofs, datalen);
+ client.setBitmap(bmpcode, new Bitmap(client, st, colorkey));
+ break;
+ }
+ case MSG_PLAYER_ICON: {
+ int pid = args[0];
+ int icocode = args[1];
+ Player p = client.getPlayer(pid);
+ p.icon = client.getIcon(icocode);
+ break;
+ }
+ case MSG_PING: {
+ buffer[ofs+1+typecodes] = CMSG_PONG;
+ sendData(buffer, ofs, base-ofs);
+ if (nargs > 0 && !client.udpovertcp) {
+ int udpkbytes = args[0];
+ /* switch to udp_over_tcp if the udp socket didn't
+ receive at least 60% of the packets sent by the server,
+ or if the socketdisplayer thread died */
+ if (sockdisplayer != null && !sockdisplayer.isAlive()) {
+ showStatus("routing UDP traffic over TCP (no UDP socket)");
+ client.start_udp_over_tcp();
+ }
+ else if (udpkbytes * 1024.0 * 0.60 > client.udpbytecounter) {
+ client.udpsock_low += 1;
+ if (client.udpsock_low >= 4) {
+ double inp =client.udpbytecounter/(udpkbytes*1024.0);
+ int loss = (int)(100.0*(1.0-inp));
+ showStatus("routing UDP traffic over TCP (" +
+ Integer.toString(loss) +
+ "% packet loss)");
+ client.start_udp_over_tcp();
+ }
+ }
+ else
+ client.udpsock_low = 0;
+ }
+ break;
+ }
+ case MSG_PONG: {
+ if (!client.taskbarfree && !client.taskbarmode) {
+ client.taskbarfree = true;
+ client.setTaskbar(true);
+ }
+ break;
+ }
+ case MSG_INLINE_FRAME: {
+ if (client.uinflater != null) {
+ int dataofs = args[0];
+ int datalen = args[1];
+ int len;
+ byte[] pkt = client.uinflater_buffer;
+ client.uinflater.setInput(buffer, dataofs, datalen);
+ try {
+ len = client.uinflater.inflate(pkt);
+ }
+ catch (DataFormatException e) {
+ len = 0;
+ }
+ Playfield pf = client.playfield;
+ if (len > 0 && pf != null) {
+ client.uinflater_buffer = pf.setSprites(pkt, len);
+ client.repaint();
+ }
+ }
+ break;
+ }
+ default: {
+ System.err.println("Note: unknown message " +
+ Byte.toString(msgcode));
+ break;
+ }
+ }
+ return base;
+ }
+
+ public void run() {
+ try {
+ byte[] buffer = new byte[0xC000];
+ int begin = 0;
+ int end = 0;
+ try {
+ while (true) {
+ if (end + 0x6000 > buffer.length) {
+ // compact buffer
+ byte[] newbuf;
+ end = end-begin;
+ if (end + 0x8000 > buffer.length)
+ newbuf = new byte[end + 0x8000];
+ else
+ newbuf = buffer;
+ System.arraycopy(buffer, begin, newbuf, 0, end);
+ begin = 0;
+ buffer = newbuf;
+ }
+ int count = socketInput.read(buffer, end, 0x6000);
+ if (isInterrupted())
+ break;
+ if (count <= 0)
+ connectionClosed();
+ end += count;
+ while ((count=decodeMessage(buffer, begin, end)) >= 0) {
+ begin = count;
+ }
+ }
+ }
+ catch (InterruptedIOException e) {
+ }
+ socket.close();
+ }
+ catch (IOException e) {
+ client.debug(e);
+ }
+ }
+ }
+
+ // UDP Socket messages
+
+ class SocketDisplayer extends Thread {
+ public static final int UDP_BUF_SIZE = 0x10000;
+
+ public pclient client;
+ public DatagramSocket socket;
+
+ SocketDisplayer(pclient aclient) {
+ setDaemon(true);
+ client = aclient;
+ }
+
+ public void run() {
+ /* This thread may die early, typically because of JVM
+ security restrictions. */
+ byte[] buffer = new byte[UDP_BUF_SIZE];
+ DatagramPacket pkt = new DatagramPacket(buffer, UDP_BUF_SIZE);
+ try {
+ {
+ socket = new DatagramSocket();
+ int[] args = new int[1];
+ args[0] = socket.getLocalPort();
+ client.socklistener.sendMessage(SocketListener.CMSG_UDP_PORT,
+ args);
+ }
+ try {
+ while (true) {
+ socket.receive(pkt);
+ if (isInterrupted())
+ break;
+ client.udpbytecounter += (double) pkt.getLength();
+ Playfield pf = client.playfield;
+ if (pf != null) {
+ pkt.setData(pf.setSprites(pkt.getData(),
+ pkt.getLength()));
+ pkt.setLength(UDP_BUF_SIZE);
+ client.repaint();
+ }
+ }
+ }
+ catch (InterruptedIOException e) {
+ }
+ socket.close();
+ }
+ catch (IOException e) {
+ client.debug(e);
+ }
+ }
+ }
+
+ // Applet methods
+
+ public SocketListener socklistener = null;
+ public SocketDisplayer sockdisplayer = null;
+ public Playfield playfield = null;
+
+ public void init() {
+ try {
+ Socket link;
+ String param;
+
+ String gamesrv = getParameter("gamesrv");
+ if (gamesrv == null) {
+ gamesrv = getDocumentBase().getHost();
+ }
+ param = getParameter("gameport");
+ if (param != null) {
+ // direct TCP connexion to the game server
+ link = new Socket(gamesrv, Integer.parseInt(param));
+ }
+ else {
+ // UCP query
+ param = getParameter("port");
+ int port = (param != null) ? Integer.parseInt(param) : defaultPort;
+ link = pickHost(gamesrv, port);
+ }
+ socklistener = new SocketListener(this, link);
+ socklistener.start();
+ }
+ catch (IOException e) {
+ debug(e);
+ }
+ }
+
+ public void destroy() {
+ if (socklistener != null) {
+ socklistener.interrupt();
+ socklistener = null;
+ }
+ }
+
+ public void start() {
+ enableEvents(AWTEvent.KEY_EVENT_MASK |
+ AWTEvent.MOUSE_EVENT_MASK |
+ AWTEvent.MOUSE_MOTION_EVENT_MASK);
+ if (socklistener != null) {
+ sockdisplayer = new SocketDisplayer(this);
+ sockdisplayer.start();
+ }
+ }
+
+ public void stop() {
+ if (sockdisplayer != null) {
+ sockdisplayer.interrupt();
+ sockdisplayer = null;
+ }
+ }
+
+ public void update(Graphics g) {
+ paint(g);
+ }
+
+ public void paint(Graphics g) {
+ Playfield pf = playfield;
+ if (pf != null) {
+ pf.paint(g);
+ }
+ else {
+ int appWidth = getSize().width;
+ int appHeight = getSize().height;
+ g.clearRect(0, 0, appWidth, appHeight);
+ }
+ }
+
+ protected void processKeyEvent(KeyEvent e) {
+ int num;
+ byte[] msg;
+ e.consume();
+ switch (e.getID()) {
+ case KeyEvent.KEY_PRESSED:
+ num = e.getKeyCode();
+ break;
+ case KeyEvent.KEY_RELEASED:
+ num = -e.getKeyCode();
+ break;
+ default:
+ return;
+ }
+ msg = (byte[]) keycodes.get(new Integer(num));
+ if (msg != null && socklistener != null) {
+ Player p = getPlayer(msg[0]);
+ if (p.local) {
+ try {
+ socklistener.sendData(msg, 1, msg.length-1);
+ }
+ catch (IOException ioe) {
+ debug(ioe);
+ }
+ return;
+ }
+ }
+ if (keydefinition_k != null && e.getID() == KeyEvent.KEY_PRESSED)
+ defineKey(num);
+ }
+
+ public void nextKey() {
+ KeyName k = keydefinition_k;
+ if (k == null)
+ k = keys;
+ else
+ k = k.next;
+ while (k != null && k.keyname.charAt(0) == '-') {
+ k.newkeycode = 0;
+ k = k.next;
+ }
+ keydefinition_k = k;
+ }
+
+ public void defineKey(int num) {
+ KeyName k;
+ for (k=keys; k!=keydefinition_k; k=k.next)
+ if (k.newkeycode == num)
+ return;
+ k.newkeycode = num;
+ nextKey();
+ if (keydefinition_k == null) {
+ if (socklistener != null) {
+ try {
+ byte[] buffer;
+ int[] args = new int[1];
+ args[0] = keydefinition_pid;
+ socklistener.sendMessage(SocketListener.CMSG_ADD_PLAYER,
+ args);
+ String param = "player" +
+ Integer.toString(keydefinition_pid);
+ param = getParameter(param);
+ if (param != null) {
+ socklistener.sendMessageEx(
+ SocketListener.CMSG_PLAYER_NAME,
+ args,
+ param);
+ }
+ args = new int[2];
+ args[0] = keydefinition_pid;
+ for (k=keys; k!=null; k=k.next) {
+ if (k.keyname.charAt(0) == '-') {
+ String test = k.keyname.substring(1);
+ for (KeyName r=keys; r!=null; r=r.next)
+ if (r.keyname.equals(test))
+ k.newkeycode = -r.newkeycode;
+ }
+ args[1] = k.keyid;
+ buffer = socklistener.codeMessage
+ (1, SocketListener.CMSG_KEY, args, null);
+ buffer[0] = (byte) keydefinition_pid;
+ keycodes.put(new Integer(k.newkeycode), buffer);
+ }
+ }
+ catch (IOException ioe) {
+ debug(ioe);
+ }
+ }
+ }
+ repaint();
+ }
+
+ protected void processMouseMotionEvent(MouseEvent e) {
+ Playfield pf = playfield;
+ if (pf != null)
+ setTaskbar(e.getY() >= pf.pfheight - pf.TASKBAR_HEIGHT);
+ e.consume();
+ }
+
+ protected void processMouseEvent(MouseEvent e) {
+ if (e.getID() != MouseEvent.MOUSE_PRESSED) {
+ e.consume();
+ return;
+ }
+ requestFocus();
+ keydefinition_k = null;
+ Playfield pf = playfield;
+ if (pf != null && e.getY() >= pf.pfheight - pf.TASKBAR_HEIGHT) {
+ int x = e.getX();
+ for (Player p=firstPlayer(); p!=null; p=nextPlayer(p))
+ if (p.xmin <= x && x < p.xmax) {
+ if (p.local) {
+ if (socklistener != null) {
+ try {
+ int[] args = new int[1];
+ args[0] = p.pid;
+ socklistener.sendMessage
+ (SocketListener.CMSG_REMOVE_PLAYER, args);
+ }
+ catch (IOException ioe) {
+ debug(ioe);
+ }
+ }
+ }
+ else {
+ keydefinition_pid = p.pid;
+ nextKey();
+ }
+ break;
+ }
+ }
+ e.consume();
+ repaint();
+ }
+
+ // UDP-over-TCP
+ public double udpbytecounter = 0.0;
+ public int udpsock_low = 0;
+ public boolean udpovertcp = false;
+ public Inflater uinflater = null;
+ public byte[] uinflater_buffer = null;
+
+ public void start_udp_over_tcp()
+ {
+ udpovertcp = true;
+ int[] args = new int[1];
+ args[0] = 0;
+ try {
+ socklistener.sendMessage(SocketListener.CMSG_UDP_PORT, args);
+ }
+ catch (IOException e) {
+ return;
+ }
+ uinflater_buffer = new byte[SocketDisplayer.UDP_BUF_SIZE];
+ uinflater = new Inflater();
+ if (sockdisplayer != null) {
+ sockdisplayer.interrupt();
+ sockdisplayer = null;
+ }
+ }
+
+// // ImageObserver interface
+
+// public boolean imageUpdate(Image img,
+// int infoflags,
+// int x,
+// int y,
+// int width,
+// int height) {
+// return false;
+// }
+}