--- /dev/null
+/* $Id: LICENSE,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2005-2007, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
--- /dev/null
+$Id: README,v 1.1 2006/12/31 08:32:39 mmondor Exp $
+
+TODO
+====
+
+SERVER
+
+- Add support for collisions between torpedoes and ships.
+- Add support for multiframe objects. For instance, detonations could have
+ multiple frames, as well as the cloaking effect. These should possibly more
+ be time-dependent than actual frame dependent however, considering that
+ frames can be delayed and their rate changed.
+ If there was a fixed client-side FPS for those, this would also mean that
+ all other objects obtained from the server would need to be recorded as
+ well... Possibly that if we did that it might also be possible to transmit
+ less frames over the network but with thrust/angle information for the client
+ to be able to evaluate smooth transitions between them...
+- Cause older torpedoes to automatically get detonated whenever a new
+ torpido is fired and the maximum number of torpedoes has been reached.
+- Implement authentication credentials checking, limit same user to one
+ connection only
+- Limit connections per ip address in number and frequency
+- Implement and test using rc4 or openssl with aes. Verify if the overhead
+ is negligeable enough to be worth keeping. Otherwise, we could use crypto-
+ graphy for authentication only if wanted.
+- Possibly implement as part of the authentication system a method to only
+ allow signed client keys, the client key would be sent to us, and then also
+ make sure that only one client using that key can connect at a time,
+ logging when it's not the case. This client-specific unique signed key
+ could be used as ID possibly? Or other than signing we perhaps should also
+ sign against the player's name and verify at login that he can only use that
+ login with that key, too.
+- Add proper connection/disconnection/events logging
+
+CLIENT
+
+- Continue test: draw objects as they are received by the server until the
+ end of frame packet, at which time we would update the display flipping the
+ double buffered pages. This also means that we must find a way to not take
+ all CPU time while waiting for both SDL events and server packets ones at
+ the same time! Since receiving is prioritary, perhaps that we could wait
+ for messages from the server using a ring, since SDL events would
+ automatically be queued meanwhile?
+ Also now send user events to the server.
+- Add an auto-ping along with fps... Use SDL_GetTicks() to calculate the
+ delay between sending ping and receiving pong.
+- Create packets to also send user updates about his ship's statistics.
+ These could probably be updated less frequently than 10 times per second.
+
+
+NOTES
+=====
+
+An attempt to develop a portable game client using SDL among unix
+and windows operating systems. The only officially supported
+compiler should be GCC (and mingw under windows, avoiding the need
+for cygwin libraries). The compiling/development environment when
+under windows will use cygwin to provide a shell, vim and cvs,
+while compile options will be specified as needed for it to use
+the mingw compiler (which also comes as part of cygwin).
+
+Initial tests will be using SDL, SDL_mixer and SDL_net.
+
+If all works well, it should also be easy to use OpenGL portably.
+I was already able to get portable SDL/OpenGL code working, a while
+ago, but this generated no sound and had no network requirements.
+Hence this test.
+
+Moreover, the test server under kqueue/ needed a client for further
+testing to be possible at its development stage.
+
+Since SDL_net does not provide non-blocking I/O (and it remains
+unclear if windows supports this properly), a decision was made to
+port to SDL an inter-thread messaging library I had done for use
+with POSIX threads, and to use multiple threads.
+
+One thread will be used to send data to the server, another thread
+to receive data form the server, yet another thread to deal with
+all user input events, and a main thread to receive all those events
+in an asynchroneous manner form the utility threads and run the
+main loop.
+
+04:21 <@lucca> one thread to send, one thread to receive, one thread to poll
+ for io events, and one thread to bring them all and in the darkness
+ bind them.
+
+:)
+
+Ogg-vorbis will be used for music, using SDL_mixer. Ship rotations
+will be performed using SDL_gfx.
+
+It is very important to avoid having to link this client statically
+against GPL or LGPL libraries, because of the viral nature of those
+licenses. I do not intend to release my code under those licenses.
+If it ever publically is released, it shall be done under a MIT/BSD
+derived license.
+
+I am also thinking about dedicating a thread for the connect state
+to the server, or possibly to have the writer or reader thread also
+perform that task.
+
+It is possible that a thread be ideal for rendering as well. It
+could be notified when a screen refresh is wanted, when it could
+set a flag. When it's time for it to draw a frame (honoring FPS),
+it would if the flag is set, or perhaps it simply could when it
+wants. I wonder if it would be appropriate for the display thread
+to not need a mutex, since it would always be read-only accessing
+the data.
+
+Anticipated design so far:
+ Main thread
+ User events thread
+ Network receive thread
+ Network Send/Connect thread
+ Display thread (the Receive thread will draw for now).
+
+It is possible that states may be desired. For instance, there
+would be the connection state, the one where the user and client
+have to provide authentication information (this could be part of
+connect phase perhaps), and in-game state.
+
+At first, as a test, the world will all be seen by everyone and
+will fit into their screen. A world of 1024x768 could be used for
+this :) Then there will probably be addition of a chat system with
+messages window, to continue enhancing the protocol. Things will
+go on from there...
+
+Hmm for now I want to simply use a joypad.
+- Button 0 fires in direction of the paddle direction.
+- Buttons 1 and 3 could act like button 0, to provide secondary weapon (1)
+ and special weapon (3).
+- Button 2 attempts to correct navigation direction in the direction
+ of the paddle.
+- Button 6 would accelerate.
+- Button 7 would decelerate.
+- Button 4 could toggle shields
+- Button 5 could toggle cloak
+
+Equivalent keyboard layout:
+- uiojlm,. would change angle just like the gamepad directions.
+- w would toggle cloak
+- s would toggle shield
+- z would thrust up
+- a would thrust down
+- d would cause direction change
+- space would torp
+- f would fire second weapon
+- g would fire special weapon
+
+And we're already out of buttons, we can't beam up/down armies or
+bomb. Unless button 3 was special instead of a special weapon,
+and allowed to perform various commands depending on the paddle
+direction (i.e. up/down to beam up/down, left to bomb). This also
+means that orbiting/launching would need to be automatic. Of course
+all this is if we're thinking about a game like netrek. But we'll
+simply only allow dogfighting at first.
+
+Now it becomes tricky how I'll minimize bandwidth sent from the
+client to the server. Probably that buttons events will be monitored,
+and then current paddle direction when required. In the case of
+direction change button, the last paddle direction applied would
+be remembered, and if the same, the event could be dropped. If
+not, send a direction change packet. What happens if a button
+remains pressed while a direction change occurs? We probably should
+ignore it.
+
+
+Threading limitations under win32
+=================================
+
+There seem to be bugs when using SDL with multiple threads under windows
+which I did not observe on unix systems. The docs specify that the main
+thread should perform the drawing, but it wasn't specified that another
+thread than the initial one would not be able to obtain all user input
+events on windows. Typed keys would not be received, for instance.
+
+It then appears that most of the processing must be done in the main
+initial thread, while only networking related blocking functions will
+be done in slave threads.
+
+There also seem to be other windows-specific problems using SDL threads,
+such as instability. I have noticed that when using another thread
+for user events reception, part of the application would often lockup,
+despite my code properly using mutexes as required, and all sound and
+greaphics being performed by the main initial thread nevertheless.
+These problems were also not found to occur on unix systems.
+
+The design was thus changed for now, and threads will be used for SDL_net
+functions only, since they are blocking. Let's hope that this will work
+stably, however. It remains to be tested.
+
+
+Storing images and sound samples as part of the executable binary
+=================================================================
+
+I was able to include read-only (.rodata) and read-write (.data) into
+binaries directly from files using objcopy and linking them on NetBSD,
+Linux and cygwin-mingw. The SDL_image library, which I now successfully
+built for mingw, includes functions that can use RWops, and the SDL
+library allows to easily create RWops from memory buffers. Moreover, the
+SDL_mixer library also allows to do this to load sound samples. I should
+thus modify the makefiles and code to very easily use these features.
+
+The SDL_mixer library however does not allow by itself to do this easily
+with music files. However, thise generally being considerably larger,
+it should not be a problem and they can remain external.
+
+If this works fine, it would be easy to generate a cryptographic block
+cipher key at build time and to include that key within the executable
+as well. Copies of the binary files to be included could then be
+encrypted using that cipher to a temporary copy which will be linked
+in, and an initialization function could be provided to unencrypt the
+files prior to use. Of course, since the key is also in the executable,
+there is no real security. However, it could prevent computer-illiterate
+people from too easily ripping our original content. RC4 could be used
+for this for instance, or even a much more simple custom encoding
+algorithm :)
+
+
+Using a map
+===========
+
+We probably want to randomize the ship's positions on the map.
+Randomization in this respect could be done on a position in a
+circle of varied radius distance to the location of the actual
+ship. This would prevent clients from being able to exactly fire
+at the actual ship position using the map (which also would make
+cloaking useful against an unofficial client).
+
+
+Encrypting communications
+=========================
+
+A stream cypher such as RC4 would be simple and efficient to encrypt
+communication between the client and the server. If RC4 is used,
+the following must however be taken into consideration:
+
+1) Session keys must always be unique. To achieve this, a function
+like HMAC to obtain from a long term key and a unique noonce could
+be used if necessary. This however is best for private key
+cryptography, I believe.
+
+2) The key must be renegotiated from time to time after enough data
+has been sent using it. There exist vulnerabilities if 1GB of data
+is transfered using the same key.
+
+3) At least 1024 bytes of the pseudo-random generator keystream
+must be discarded after key creation to avoid a key predictability
+vulnerability.
+
+If public key cryptography was used, at least 256-bit RSA could be
+used. Such an implemetation theoretically would not need larger
+numbers than 128-bit ones to function, I think. Then a "complex"
+bignum library could probably be used which is faster and simpler
+than a general purpose more complex arbitrary precision math library.
+
+We probably should borrow NetBSD's SHA512 (or at least SHA1) and RMD160
+algorithms. My ARC4 implementation could be used as well.
+
+
+Compressing communications
+==========================
+
+Investigate if using huffman compression would be wanted and worth
+it, or some other simple compression system suiting well to the
+transfered data. Possibly that the dictionary could be static and
+would never need to be transfered.
+
+zlib could probably also be used. It however unfortunately is
+famous for frequently discovered vulnerabilities.
+
+
+
+PROGRAMMING STYLE
+=================
+
+The style chosen for this project consists of the (Net)BSD KNF style
+(Kernel Normal Form) style borrowed from. All code should conform to it.
+Additionally, lint(1)-style comments are used to make the code clearer
+for future code auditors.
+
+
+
+Matt
--- /dev/null
+# $Id: GNUmakefile,v 1.1 2006/12/31 08:32:39 mmondor Exp $
+
+CC := cc
+RM := rm
+UNAME := uname
+TOUCH := touch
+OBJDUMP := objdump
+OBJCOPY := objcopy
+GREP := grep
+AWK := awk
+DATE := date
+STRIP := strip
+
+TMPDIR := /tmp
+
+CFLAGS += -Wall -Isrc -I../common
+
+# Enable for verbosity/debugging
+#CFLAGS += -v -H -g
+#LDFLAGS += -v -g
+
+# And to disable assertions
+CFLAGS += -DNDEBUG
+#CFLAGS += -g
+
+# And to enable profiling
+#CFLAGS += -pg
+#LDFLAGS += -pg
+
+OBJS := $(addprefix src/,main.o debug.o pool.o packets.o thread_msg.o \
+ thread_net_recv.o recvq.o thread_net_send.o screen.o decode.o enc.o) \
+ $(addprefix ../common/,sha1.o rmd160.o hmac_sha1.o hmac_rmd160.o \
+ mmenc.o)
+BIN := tms-client
+RAWOBJS := $(addsuffix .enc.o,$(addprefix bmp/,RomDD.bmp FedCA.bmp) \
+ $(addprefix wav/,nt_cloaked.wav nt_uncloak.wav nt_shield_up.wav \
+ nt_shield_down.wav nt_fire_torp_other.wav nt_plasma_hit.wav \
+ nt_explosion_other.wav) \
+ $(addprefix fnt/,7x13.fnt))
+
+SDL_CFLAGS := $(shell sdl-config --cflags)
+SDL_LDFLAGS := $(shell sdl-config --libs)
+SDL_LDFLAGS += -lSDL_image -lSDL_mixer -lSDL_net -lSDL_gfx
+
+Z_CFLAGS := -I/usr/local/include
+Z_LDFLAGS := -lz
+
+# OS dependent settings follow
+OS := $(shell $(UNAME) -s)
+ifneq (,$(findstring CYGWIN,$(OS)))
+ # cygwin-mingw
+ CFLAGS += -mno-cygwin -I/usr/include/mingw -DWIN32
+ LDFLAGS += -mwindows -mno-cygwin -L/usr/lib/mingw -L/usr/local/lib
+# GL_CFLAGS :=
+# GL_LDFLAGS := -lopengl32 -lglu32
+else
+ # unix
+ CFLAGS += -I/usr/include -I/usr/pkg/include -I/usr/X11R6/include
+ LDFLAGS += -L/usr/lib -L/usr/pkg/lib -L/usr/X11R6/lib
+# GL_CFLAGS :=
+# GL_LDFLAGS := -lGL -lGLU
+endif
+
+# Determine target of compiled objects so that we may convert binaries
+# to compatible objects using objcopy and then link them like other modules.
+OBJTARGET := $(shell $(TOUCH) $(TMPDIR)/obj.c && \
+ $(CC) $(CFLAGS) -c -o $(TMPDIR)/obj.o $(TMPDIR)/obj.c && \
+ $(OBJDUMP) -t $(TMPDIR)/obj.o | \
+ $(GREP) 'file format' | $(AWK) '{print $$4}' \
+ && $(RM) $(TMPDIR)/obj.o $(TMPDIR)/obj.c)
+OBJARCH := $(shell echo $(OBJTARGET) | $(AWK) -F '-' '{print $$2}')
+SEED := $(shell date +%s)
+
+# Architecture independent settings follow
+CFLAGS += -DBIG_ENDIAN=4321 -DLITTLE_ENDIAN=1234
+ifeq ($(OBJARCH),i386)
+ CFLAGS += -DBYTE_ORDER=LITTLE_ENDIAN
+else
+ CFLAGS += -DBYTE_ORDER=BIG_ENDIAN
+endif
+
+#CFLAGS += $(SDL_CFLAGS) $(GL_CFLAGS)
+#LDFLAGS += $(SDL_LDFLAGS) $(GL_LDFLAGS)
+CFLAGS += $(SDL_CFLAGS) $(Z_CFLAGS)
+LDFLAGS += $(SDL_LDFLAGS) $(Z_LDFLAGS)
+
+all: $(BIN)
+
+%.o: %.c
+ $(CC) -c $(CFLAGS) -I. -o $@ $<
+
+encode:
+ $(CC) -o src/encode src/encode.c
+
+$(RAWOBJS): encode
+ src/encode $(basename $(basename $@)) $(basename $@) $(SEED)
+ $(OBJCOPY) -I binary -B $(OBJARCH) -O $(OBJTARGET) $(basename $@) $@
+ $(RM) -f $(basename $@)
+
+$(BIN): $(OBJS) $(RAWOBJS)
+ $(CC) -o $@ $(OBJS) $(RAWOBJS) $(LDFLAGS)
+ $(STRIP) -s -w -R .comment -R .ident -R .debug* $@*
+
+clean:
+ $(RM) -f $(BIN) $(BIN).exe $(OBJS) $(RAWOBJS) \
+ src/encode src/encode.exe stdout.txt stderr.txt
--- /dev/null
+/* $Id: conf.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+#ifndef _CONF_H_
+#define _CONF_H_
+
+
+
+/*
+ * Various hardcoded configuration parameters.
+ */
+
+#define SERVER_HOST "hal.xisop"
+/*#define SERVER_HOST "tms-play.pulsar-zone.net"*/
+
+#define SERVER_PORT 7777
+
+#define CLIENT_VERSION 1
+#define CLIENT_LOGIN "login"
+#define CLIENT_PASSWD "d9d19c4285a4782a1b231d98aff232a046978cc2"
+
+#define USE_COMPRESSION
+#define USE_ENCRYPTION
+
+
+
+#endif
--- /dev/null
+/* $Id: debug.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+#ifndef NDEBUG
+void
+debug(const char *file, const char *func, int line, const char *fmt, ...)
+{
+ va_list lst;
+ char buf[1024];
+
+ va_start(lst, fmt);
+ vsnprintf(buf, 1023, fmt, lst);
+ va_end(lst);
+ (void) fprintf(stderr, "%s:%s():%d - %s\n", file, func, line, buf);
+}
+
+void
+debug2(const char *file, const char *func, int line, const char *fmt, ...)
+{
+ va_list lst;
+ char buf[1024];
+
+ va_start(lst, fmt);
+ vsnprintf(buf, 1023, fmt, lst);
+ va_end(lst);
+ (void) fprintf(stderr, "%s:%s():%d - %s\n", file, func, line, buf);
+
+ exit(EXIT_FAILURE);
+}
+#endif
--- /dev/null
+/* $Id: debug.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+
+
+#ifndef NDEBUG
+
+/*
+ * Macro similar to assert(3) but which does not exit the application. Will
+ * instead log the condition unless DEBUG_ASSERT_ABORT is set.
+ * Moreover, the aborting one actually simply calls exit(2) after logging the
+ * error instead of generating a SIGABRT signal.
+ */
+#ifdef ASSERT_ABORT
+#define ASSERT(c) if (!(c)) \
+ debug2(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c);
+#else
+#define ASSERT(c) if (!(c)) \
+ debug(__FILE__, __func__, __LINE__, "ASSERT(" #c ") == %d", c);
+#endif
+
+#else
+
+#define ASSERT(c) ;
+
+#endif
+
+
+
+void debug(const char *, const char *, int, const char *, ...);
+void debug2(const char *, const char *, int, const char *, ...);
+
+
+
+#endif
--- /dev/null
+/* $Id: decode.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Function to easily decode data processed by the encode command.
+ */
+
+
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <decode.h>
+
+
+
+void
+decode(void **ndata, size_t *nsize, void *data, size_t size)
+{
+ uint8_t *ptr, *tptr, *key;
+
+ ptr = (uint8_t *)data;
+ *ndata = &ptr[4];
+ *nsize = size - 4;
+
+ for (key = ptr, ptr = &ptr[4], tptr = &ptr[*nsize];
+ ptr < tptr; ptr++) {
+ *ptr ^= key[0] ^ key[1] ^ key[2] ^ key[3];
+ key[0]--;
+ key[1]++;
+ key[2] -= 3;
+ key[3] += 3;
+ }
+}
--- /dev/null
+/* $Id: decode.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Function to easily decode data processed by the encode command.
+ */
+
+
+
+#ifndef DECODE_H
+#define DECODE_H
+
+
+
+void decode(void **, size_t *, void *, size_t);
+
+
+
+#endif
--- /dev/null
+/* $Id: dlist.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2001-2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#ifndef DLIST_H
+#define DLIST_H
+
+
+
+typedef struct list list_t;
+typedef struct node node_t;
+
+
+
+struct node {
+ node_t *prev, *next;
+};
+
+struct list {
+ node_t *top, *bottom;
+ int nodes;
+};
+
+
+
+/* Some macros to optimize operations on doubly linked lists */
+#define DLIST_INITIALIZER {NULL, NULL, 0}
+
+#define DLIST_INIT(lst) do { \
+ (lst)->top = (lst)->bottom = NULL; \
+ (lst)->nodes = 0; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_UNLINK(lst, nod) do { \
+ register node_t *prev = (nod)->prev, *next = (nod)->next; \
+ \
+ if (prev != NULL) \
+ prev->next = next; \
+ else \
+ (lst)->top = next; \
+ if (next != NULL) \
+ next->prev = prev; \
+ else \
+ (lst)->bottom = prev; \
+ (lst)->nodes--; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_APPEND(lst, nod) do { \
+ register node_t *tmp = (lst)->bottom; \
+ \
+ if (tmp != NULL) { \
+ tmp->next = (nod); \
+ (nod)->prev = tmp; \
+ (nod)->next = NULL; \
+ (lst)->bottom = (nod); \
+ } else { \
+ (lst)->bottom = (lst)->top = (nod); \
+ (nod)->next = (nod)->prev = NULL; \
+ } \
+ (lst)->nodes++; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_INSERT(lst, nod) do { \
+ register node_t *tmp = (lst)->top; \
+ \
+ if (tmp != NULL) { \
+ tmp->prev = (nod); \
+ (nod)->prev = NULL; \
+ (nod)->next = tmp; \
+ (lst)->top = (nod); \
+ } else { \
+ (lst)->top = (lst)->bottom = (nod); \
+ (nod)->next = (nod)->prev = NULL; \
+ } \
+ (lst)->nodes++; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_INSERTAT(lst, atnode, nod) do { \
+ register node_t *prev = (atnode)->prev, *next = (atnode); \
+ \
+ (nod)->next = next; \
+ next->prev = (nod); \
+ if (prev != NULL) { \
+ prev->next = (nod); \
+ (nod)->prev = prev; \
+ } else { \
+ (lst)->top = (nod); \
+ (nod)->prev = NULL; \
+ } \
+ (lst)->nodes++; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_SWAP(dst, src, nod, ins) do { \
+ register node_t *prev = (nod)->prev, *next = (nod)->next; \
+ \
+ if (prev != NULL) \
+ prev->next = next; \
+ else \
+ (src)->top = next; \
+ if (next != NULL) \
+ next->prev = prev; \
+ else \
+ (src)->bottom = prev; \
+ (src)->nodes--; \
+ if ((ins)) { \
+ if ((prev = (dst)->top) != NULL) { \
+ prev->prev = (nod); \
+ (nod)->prev = NULL; \
+ (nod)->next = prev; \
+ (dst)->top = (nod); \
+ } else { \
+ (dst)->top = (dst)->bottom = (nod); \
+ (nod)->next = (nod)->prev = NULL; \
+ } \
+ } else { \
+ if ((prev = (dst)->bottom) != NULL) { \
+ prev->next = (nod); \
+ (nod)->prev = prev; \
+ (nod)->next = NULL; \
+ (dst)->bottom = (nod); \
+ } else { \
+ (dst)->bottom = (dst)->top = (nod); \
+ (nod)->next = (nod)->prev = NULL; \
+ } \
+ } \
+ (dst)->nodes++; \
+} while (/* CONSTCOND */0)
+
+#define DLIST_TOP(lst) ((void *)((list_t *)(lst))->top)
+#define DLIST_BOTTOM(lst) ((void *)((list_t *)(lst))->bottom)
+#define DLIST_NEXT(var) ((void *)((node_t *)(var))->next)
+#define DLIST_PREV(var) ((void *)((node_t *)(var))->prev)
+
+#define DLIST_FOREACH(lst, var) \
+ for ((var) = DLIST_TOP((lst)); (var) != NULL; (var) = DLIST_NEXT((var)))
+
+#define DLIST_NODES(lst) (((list_t *)(lst))->nodes)
+
+
+
+#endif
--- /dev/null
+/* $Id: enc.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <conf.h>
+
+
+
+#ifdef USE_ENCRYPTION
+
+
+
+#include <enc.h>
+
+
+
+mmenc_t enc_in, enc_out;
+int mmencrypt = 0;
+
+
+
+#endif
--- /dev/null
+/* $Id: enc.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+#ifndef _ENC_H_
+#define _ENC_H_
+
+
+
+#include <mmenc.h>
+
+
+
+extern mmenc_t enc_in, enc_out;
+extern int mmencrypt;
+
+
+
+#endif
--- /dev/null
+/* $Id: encode.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHT RESERVED.
+ */
+
+/*
+ * Very simple encoding algorithm. Given a 32-bit key,
+ * it will encode the supplied file using XOR encoding and changing
+ * the integer values.
+ * For even more simplicity, the key is randomly generated and stored as
+ * part of the output file as the first four bytes :)
+ * This really shouldn't be considered encryption, it merely is simple
+ * encoding for computer-illiterates to not too easily rip our images
+ * and sound samples.
+ */
+
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+#define BUF_SIZE 65536
+
+
+
+int main(int, char **);
+void encode(uint8_t *, size_t, uint8_t *key);
+
+
+
+int
+main(int argc, char **argv)
+{
+ FILE *ifh, *ofh;
+ uint8_t key[4], *buf;
+ size_t s;
+
+ if (argc != 4) {
+ (void) fprintf(stderr,
+ "Usage: encode <infile> <outfile> <seed>\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((buf = malloc(BUF_SIZE)) == NULL) {
+ (void) fprintf(stderr,
+ "Could not allocate %d bytes\n", BUF_SIZE);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((ifh = fopen(argv[1], "r")) == NULL) {
+ (void) fprintf(stderr,
+ "Cannot open '%s' for reading\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((ofh = fopen(argv[2], "w")) == NULL) {
+ (void) fprintf(stderr,
+ "Cannot open '%s' for writing\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Generate random key and write it to output file */
+ srand((unsigned int)strtoul(argv[3], NULL, 10));
+ key[0] = rand() & 0xff;
+ key[1] = rand() & 0xff;
+ key[2] = rand() & 0xff;
+ key[3] = rand() & 0xff;
+ if (fwrite(key, 1, 4, ofh) != 4) {
+ (void) fprintf(stderr,
+ "Error writing key to '%s'\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+
+ while ((s = fread(buf, 1, BUF_SIZE, ifh)) > 0) {
+ encode(buf, s, key);
+ if (fwrite(buf, 1, s, ofh) != s) {
+ (void) fprintf(stderr,
+ "Error writing to '%s'\n", argv[2]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ (void) fclose(ofh);
+ (void) fclose(ifh);
+ free(buf);
+
+ exit(EXIT_SUCCESS);
+}
+
+void
+encode(uint8_t *data, size_t size, uint8_t *key)
+{
+ uint8_t *tdata;
+
+ for (tdata = data + size; data < tdata; data++) {
+ *data ^= key[0] ^ key[1] ^ key[2] ^ key[3];
+ key[0]--;
+ key[1]++;
+ key[2] -= 3;
+ key[3] += 3;
+ }
+}
--- /dev/null
+/* $Id: endian.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2004-2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Efficient macros for working with endian issues.
+ */
+
+
+
+#ifndef _MM_ENDIAN_H_
+#define _MM_ENDIAN_H_
+
+
+
+#include <stdint.h>
+
+#include <SDL.h>
+#include <SDL_endian.h>
+
+
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+
+/* Little endian is not network order, we need conversions */
+#define BYTEORDER_NETWORK16 SDL_Swap16
+#define BYTEORDER_HOST16 SDL_Swap16
+#define BYTEORDER_NETWORK32 SDL_Swap32
+#define BYTEORDER_HOST32 SDL_Swap32
+#define BYTEORDER_NETWORK64 SDL_Swap64
+#define BYTEORDER_HOST64 SDL_Swap64
+
+#elif SDL_BYTEORDER == SDL_BIG_ENDIAN
+
+/* Big endian is network order, no need to do anything */
+#define BYTEORDER_NETWORK16(w) (w)
+#define BYTEORDER_HOST16(w) (w)
+#define BYTEORDER_NETWORK32(w) (w)
+#define BYTEORDER_HOST32(w) (w)
+#define BYTEORDER_NETWORK64(w) (w)
+#define BYTEORDER_HOST64(w) (w)
+
+#else
+#error "Undefined SDL_BYTEORDER";
+#endif
+
+
+
+#endif
--- /dev/null
+/* $Id: main.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+/* STANDARD HEADERS */
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+/* XXX UNIX DEBUG */
+#include <signal.h>
+
+/* THIRD PARTY LIBRARY HEADERS */
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_thread.h>
+#include <SDL_rotozoom.h>
+#include <SDL_gfxPrimitives.h>
+#include <SDL_framerate.h>
+#include <SDL_mixer.h>
+#include <SDL_net.h>
+
+/* APPLICATION HEADERS */
+#include <main.h>
+#include <debug.h>
+#include <screen.h>
+#include <packets_common.h>
+#include <packets.h>
+#include <thread_msg.h>
+#include <thread_net_recv.h>
+#include <thread_net_send.h>
+#include <rawobjs.h>
+#include <decode.h>
+
+
+
+/* DEFINITIONS */
+
+enum userevents {
+ UE_TORP = 0,
+ UE_PHASER,
+ UE_PLASMA,
+ UE_DIRECTION,
+ UE_SHIELD,
+ UE_CLOAK,
+ UE_THRUST_ACC,
+ UE_THRUST_DEC
+};
+
+struct font {
+ int w, h;
+ void *data;
+ size_t size;
+};
+
+#define VECTOR_X(x, a, r) ((int)(x) + (cos_table[(a)] * (r)))
+#define VECTOR_Y(y, a, r) ((int)(y) + (sin_table[(a)] * (r)))
+
+
+
+/* PRIVATE PROTOTYPES */
+
+int main(int, char **);
+
+static void axis_angle_update(void);
+static void handle_uevent(SDL_Event *);
+static void send_event(int, ...);
+
+static SDL_Surface *bmp_load_key(void *, size_t);
+static int surface_blit_angle(SDL_Surface *, int, int, int);
+
+static Mix_Chunk *sample_load(void *, size_t);
+
+static struct font *font_load(void *, size_t, int, int);
+static void font_blit_string(struct font *, int, int,
+ const char *, uint8_t, uint8_t, uint8_t, uint8_t);
+
+static Uint32 interval_callback(Uint32, void *);
+
+static void trig_init(void);
+
+
+
+/* PUBLIC GLOBALS */
+
+thread_port_t main_port;
+
+char pingstr[8];
+Uint32 pingticks;
+
+char shipinfostr[64];
+
+
+
+/* PRIVATE GLOBALS */
+
+static int main_quit = 0;
+
+static int joy_angle = 0;
+
+static SDL_Surface *rship, *fship;
+static int shields = 1, cloaked = 0;
+
+static Mix_Chunk *snd_cloak, *snd_uncloak, *snd_shield, *snd_unshield,
+ *snd_torp, *snd_hit, *snd_explode;
+
+static SDL_TimerID interval;
+static char fpsstr[8];
+static int fpscnt;
+
+static struct font *font;
+
+static double cos_table[256], sin_table[256];
+
+
+
+/* PRIVATE FUNCTIONS */
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+ thread_ring_t main_ring;
+ SDL_Thread *recv_threadid, *send_threadid;
+ Mix_Music *mus;
+ int pending_many;
+
+ /* Initialization */
+ screen_init();
+
+ /* XXX UNIX DEBUGGING! */
+ signal(SIGSEGV, SIG_DFL);
+
+ trig_init();
+ (void) SDL_ShowCursor(0);
+ (void) SDL_EnableKeyRepeat(0, 0);
+
+ /*
+ * Ignore a few events with potentially high frequency but which
+ * we don't need
+ */
+ (void) SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+ (void) SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE);
+ (void) SDL_EventState(SDL_JOYBALLMOTION, SDL_IGNORE);
+ (void) SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE);
+ (void) SDL_EventState(SDL_KEYUP, SDL_IGNORE);
+
+ /*
+ * Network
+ */
+ if (SDLNet_Init() != 0) {
+ (void) fprintf(stderr, "main() - SDLNet_Init() - %s\n",
+ SDLNet_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Audio
+ */
+ if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) != 0) {
+ (void) fprintf(stderr, "main() - Mix_OpenAudio() - %s\n",
+ Mix_GetError());
+ exit(EXIT_FAILURE);
+ }
+ snd_cloak = sample_load((void *)&_binary_wav_nt_cloaked_wav_enc_start,
+ (size_t)&_binary_wav_nt_cloaked_wav_enc_size);
+ snd_uncloak = sample_load(
+ (void *)&_binary_wav_nt_uncloak_wav_enc_start,
+ (size_t)&_binary_wav_nt_uncloak_wav_enc_size);
+ snd_shield = sample_load(
+ (void *)&_binary_wav_nt_shield_up_wav_enc_start,
+ (size_t)&_binary_wav_nt_shield_up_wav_enc_size);
+ snd_unshield = sample_load(
+ (void *)&_binary_wav_nt_shield_down_wav_enc_start,
+ (size_t)&_binary_wav_nt_shield_down_wav_enc_size);
+ snd_torp = sample_load(
+ (void *)&_binary_wav_nt_fire_torp_other_wav_enc_start,
+ (size_t)&_binary_wav_nt_fire_torp_other_wav_enc_size);
+ snd_hit = sample_load((void *)&_binary_wav_nt_plasma_hit_wav_enc_start,
+ (size_t)&_binary_wav_nt_plasma_hit_wav_enc_size);
+ snd_explode = sample_load(
+ (void *)&_binary_wav_nt_explosion_other_wav_enc_start,
+ (size_t)&_binary_wav_nt_explosion_other_wav_enc_size);
+ (void) Mix_AllocateChannels(16);
+ (void) Mix_ReserveChannels(2);
+ if ((mus = Mix_LoadMUS("ogg/1.ogg")) == NULL) {
+ (void) fprintf(stderr, "main() - Mix_LoadMUS() - %s\n",
+ Mix_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if (Mix_PlayMusic(mus, -1) != 0) {
+ (void) fprintf(stderr, "main() - Mix_PlayMusic() - %s\n",
+ Mix_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Bitmap graphics
+ */
+ rship = bmp_load_key((void *)&_binary_bmp_RomDD_bmp_enc_start,
+ (size_t)&_binary_bmp_RomDD_bmp_enc_size);
+ fship = bmp_load_key((void *)&_binary_bmp_FedCA_bmp_enc_start,
+ (size_t)&_binary_bmp_FedCA_bmp_enc_size);
+
+ /*
+ * Bitmap fonts
+ */
+ font = font_load((void *)&_binary_fnt_7x13_fnt_enc_start,
+ (size_t)&_binary_fnt_7x13_fnt_enc_size, 7, 13);
+
+ /*
+ * We're already the main thread.
+ * Initialize our message port and notification ring.
+ */
+ if (thread_ring_init(&main_ring) == -1) {
+ (void) fprintf(stderr,
+ "main() - thread_ring_init(main_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if (thread_port_init(&main_port) == -1) {
+ (void) fprintf(stderr,
+ "main() - thread_port_init(main_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ thread_port_set_ring(&main_port, &main_ring);
+
+ /* XXX We should obtain user login information */
+
+ if (thread_amsg_pool_init() != 0) {
+ (void) fprintf(stderr, "main() - thread_amsg_init()\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Launch network utility threads */
+ if ((recv_threadid = SDL_CreateThread(thread_net_recv, NULL))
+ == NULL) {
+ (void) fprintf(stderr,
+ "main() - SDL_CreateThread(thread_net_recv) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if ((send_threadid = SDL_CreateThread(thread_net_send, NULL))
+ == NULL) {
+ (void) fprintf(stderr,
+ "main() - SDL_CreateThread(thread_net_send) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * XXX Wait until we receive the server's connection status.
+ * We should display a "connecting to server" or such message to the
+ * user during this time.
+ */
+ {
+ struct msg_connect *cmsg;
+
+ while ((cmsg = (struct msg_connect *)thread_msg_get(
+ &main_port)) == NULL)
+ (void) thread_ring_wait(&main_ring, -1);
+
+ if (cmsg->status == -1) {
+ (void) fprintf(stderr, "%s\n", cmsg->error);
+ exit(EXIT_FAILURE);
+ }
+
+ (void) thread_msg_reply(&cmsg->msg);
+ }
+
+ /* FPS and ping counter and initialization */
+ (void) strcpy(fpsstr, "000 fps");
+ (void) strcpy(pingstr, "0000 ms");
+ fpscnt = 0;
+ interval = SDL_AddTimer(5000, interval_callback, NULL);
+
+ /*
+ * Main loop.
+ * Listen for messages and events and process them, while drawing
+ * frames.
+ *
+ * XXX There remain design choices to make here.
+ * We could only draw a frame once we received all data for a frame
+ * from the server, thus waiting for the EndOfFrame message to draw,
+ * in which case we only need to bother drawing the current new
+ * received data. If we did this, we potentially could reduce the
+ * incomming asynchroneous messages rate so that a single one is sent
+ * once every messages for a frame were obtained. This method would
+ * probably be the most efficient, while allowing the client to update
+ * its display as fast as it is able to obtain the server information
+ * for a frame. However, this also means that for a very large world,
+ * if a map exists or such, there could be more data needing to be
+ * sent per frame, unless there also were in a frame the information
+ * to update the existing world information, which would be similar to
+ * the second method. Hmm since we need to send a difference packet
+ * for every object on the map, or to possibly resend their position,
+ * what could be done is having the server sending updates less
+ * frequently for that data. Although, we would need to make sure to
+ * avoid causing lag to the normal 10fps display when
+ * sending/processing large map packets. We possibly could
+ * intermingle some map information data per normal frame, having the
+ * server round-robin the map information among the clients in a
+ * distributed way? Say we have 50 connected clients, and that we
+ * want a map update rate of approximately 1 second interval, we could
+ * send the data among them at 50 / 10, meaning that we send each
+ * client update of 5 clients. With round robin this means that every
+ * client, 10 times per second are receiving enough update information
+ * so that within a second the whole map be updated. Of course, we
+ * would need not to break this when clients connect/disconnect.
+ *
+ * Or, we could instead maintain our own known world and display
+ * states and only update them via the messages received from the
+ * server, allowing us to maintain a steady frame rate (although this
+ * would also mean that no change could be made between certain
+ * frames).
+ */
+ pending_many = 0;
+ while (!main_quit) {
+ thread_amsg_t *amsg;
+ SDL_Event ev;
+ int pending;
+
+ /* Process incomming server messages. */
+ /*
+ * XXX If receiving packets and drawing them is too CPU
+ * intensive, the client can hang here if the loop was
+ * infinite. Possibly that receiving complete frame data
+ * rather than single messages would cause less overhead.
+ * However it could possibly cause problems because of the
+ * packets which need to be aligned... Should we allow
+ * clients at 20fps and fallback to 10fps for slower boxes?
+ * I actually could support 20, 10, 5, and use heuristics for
+ * client to automatically send a slow down request to the
+ * server as necessary. Perhaps also request for faster
+ * updates when we notice that there's really no overload?
+ * How can we evaluate this efficiently?
+ * Checking if queue accumulates or not? And/or, checking the
+ * delay between two EndOfFrame packets?
+ */
+ if ((amsg = (thread_amsg_t *)thread_msg_get(&main_port)) !=
+ NULL) {
+ spackets_handle(amsg);
+ thread_amsg_destroy(amsg);
+ }
+
+ /* Update gamepad current angle. */
+ if (gamepad != NULL)
+ axis_angle_update();
+
+ /*
+ * Process incomming user events, sending corresponding
+ * messages to the server when appropriate.
+ */
+ while (SDL_PollEvent(&ev))
+ handle_uevent(&ev);
+
+ if ((pending = thread_port_pending(&main_port)) == 0) {
+ pending_many = 0;
+ /*
+ * XXX On wincrap it appears that the reading thread
+ * can block indefinitely and thus -1 will freeze.
+ */
+ (void) thread_ring_wait(&main_ring, 100);
+ } else {
+ if (++pending_many > 2) {
+ cpacket_slow_send();
+ pending_many = 0;
+ /* Drop pending frames */
+ while ((amsg = (thread_amsg_t *)thread_msg_get(
+ &main_port)) != NULL)
+ thread_amsg_destroy(amsg);
+ } else {
+ /* Drop single frame */
+ if ((amsg = (thread_amsg_t *)thread_msg_get(
+ &main_port)) != NULL)
+ thread_amsg_destroy(amsg);
+ }
+ }
+ }
+
+ /* Fadeout music */
+ (void) Mix_FadeOutMusic(1000);
+ SDL_Delay(1000);
+
+ exit(EXIT_SUCCESS);
+}
+
+static void
+axis_angle_update(void)
+{
+ int angle, x, y;
+
+ angle = joy_angle;
+
+ /* On win32 -1 is reported for dead state instead of 0! */
+ if ((x = SDL_JoystickGetAxis(gamepad, 0)) > 127)
+ x = 1;
+ else if (x < -128)
+ x = -1;
+ else
+ x = 0;
+
+ if ((y = SDL_JoystickGetAxis(gamepad, 1)) > 127)
+ y = 1;
+ else if (y < -128)
+ y = -1;
+ else
+ y = 0;
+
+ if (x == 0 && y == 0)
+ return;
+
+ if (x == 0 && y == -1)
+ angle = 192;
+ else if (x == 1 && y == -1)
+ angle = 224;
+ else if (x == 1 && y == 0)
+ angle = 0;
+ else if (x == 1 && y == 1)
+ angle = 32;
+ else if (x == 0 && y == 1)
+ angle = 64;
+ else if (x == -1 && y == 1)
+ angle = 96;
+ else if (x == -1 && y == 0)
+ angle = 128;
+ else if (x == -1 && y == -1)
+ angle = 160;
+
+ if (joy_angle != angle)
+ joy_angle = angle;
+
+ return;
+}
+
+static void
+handle_uevent(SDL_Event *ev)
+{
+
+ if (ev->type == SDL_MOUSEMOTION)
+ return;
+
+ /*
+ * Quit commands events.
+ */
+ if ((ev->type == SDL_KEYDOWN && ev->key.keysym.sym == SDLK_ESCAPE) ||
+ ev->type == SDL_QUIT) {
+ main_quit = 1;
+ return;
+ }
+
+ /*
+ * Button events. joy_angle affects several of them.
+ * We currently ignore release events.
+ */
+ if (ev->type == SDL_JOYBUTTONDOWN) {
+
+ if (ev->jbutton.state != 1)
+ return;
+
+ switch (ev->jbutton.button) {
+ case 0:
+ send_event(UE_TORP);
+ break;
+ case 1:
+ send_event(UE_PHASER);
+ break;
+ case 2:
+ send_event(UE_DIRECTION, joy_angle);
+ break;
+ case 3:
+ send_event(UE_PLASMA);
+ break;
+ case 4:
+ send_event(UE_SHIELD);
+ break;
+ case 5:
+ send_event(UE_CLOAK);
+ break;
+ case 6:
+ send_event(UE_THRUST_ACC);
+ break;
+ case 7:
+ send_event(UE_THRUST_DEC);
+ break;
+ }
+
+ return;
+ }
+
+ /*
+ * Keyboard events
+ */
+ if (ev->type == SDL_KEYDOWN) {
+ int angle = joy_angle;
+
+ switch (ev->key.keysym.sym) {
+ /* Angle changes */
+ case SDLK_i:
+ angle = 192;
+ break;
+ case SDLK_o:
+ angle = 224;
+ break;
+ case SDLK_l:
+ angle = 0;
+ break;
+ case SDLK_PERIOD:
+ angle = 32;
+ break;
+ case SDLK_COMMA:
+ angle = 64;
+ break;
+ case SDLK_m:
+ angle = 96;
+ break;
+ case SDLK_j:
+ angle = 128;
+ break;
+ case SDLK_u:
+ angle = 160;
+ break;
+ /* Navigation change */
+ case SDLK_d:
+ send_event(UE_DIRECTION, joy_angle);
+ break;
+ case SDLK_z:
+ send_event(UE_THRUST_ACC);
+ break;
+ case SDLK_a:
+ send_event(UE_THRUST_DEC);
+ break;
+ /* Weapons */
+ case SDLK_SPACE:
+ send_event(UE_TORP);
+ break;
+ case SDLK_f:
+ send_event(UE_PHASER);
+ break;
+ case SDLK_g:
+ send_event(UE_PLASMA);
+ break;
+ /* Toggles */
+ case SDLK_s:
+ send_event(UE_SHIELD);
+ break;
+ case SDLK_w:
+ send_event(UE_CLOAK);
+ break;
+ default:
+ break;
+ }
+
+ if (joy_angle != angle)
+ joy_angle = angle;
+ return;
+ }
+
+ return;
+}
+
+static void
+send_event(int type, ...)
+{
+ int ch, err = 0;
+
+ switch (type) {
+ case UE_DIRECTION:
+ err = cpacket_direction_send(((int *)&type)[1]);
+ break;
+ case UE_TORP:
+ if ((ch = Mix_PlayChannel(-1, snd_torp, 0)) != -1)
+ Mix_SetPosition(ch, joy_angle, 50);
+ err = cpacket_torp_send(joy_angle);
+ break;
+ case UE_PHASER:
+ /* XXX */
+ if ((ch = Mix_PlayChannel(-1, snd_hit, 0)) != -1)
+ Mix_SetPosition(ch, joy_angle, 50);
+ break;
+ case UE_PLASMA:
+ /* XXX */
+ if ((ch = Mix_PlayChannel(-1, snd_explode, 0)) != -1)
+ Mix_SetPosition(ch, joy_angle, 50);
+ break;
+ case UE_SHIELD:
+ shields = (shields == 0 ? 1 : 0);
+ Mix_PlayChannel(1,
+ (shields == 1 ? snd_shield : snd_unshield), 0);
+ err = cpacket_shield_send();
+ break;
+ case UE_CLOAK:
+ cloaked = (cloaked == 0 ? 1 : 0);
+ Mix_PlayChannel(2,
+ (cloaked == 1 ? snd_cloak : snd_uncloak), 0);
+ err = cpacket_cloak_send();
+ break;
+ case UE_THRUST_ACC:
+ err = cpacket_thrust_acc_send();
+ break;
+ case UE_THRUST_DEC:
+ err = cpacket_thrust_dec_send();
+ break;
+ }
+
+ if (err == -1) {
+ (void) fprintf(stderr, "Error writing to server socket\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* Extern, called by spacket_eof_handler */
+void
+frame_switch(void)
+{
+
+ /* Draw text stats */
+ font_blit_string(font, 17, 461, fpsstr, 0x00, 0x00, 0x00, 0xff);
+ font_blit_string(font, 16, 460, fpsstr, 0xff, 0xff, 0xff, 0xff);
+ font_blit_string(font, 101, 461, pingstr, 0x00, 0x00, 0x00, 0xff);
+ font_blit_string(font, 100, 460, pingstr, 0xff, 0xff, 0xff, 0xff);
+ font_blit_string(font, 201, 461, shipinfostr, 0x00, 0x00, 0x00, 0xff);
+ font_blit_string(font, 200, 460, shipinfostr, 0xff, 0xff, 0xff, 0xff);
+
+ /* Switch buffers */
+ (void) SDL_Flip(screen_surface);
+
+ /* Clear new buffer */
+ /*
+ {
+ uint32_t *ptr, *tptr;
+
+ if (SDL_MUSTLOCK(screen_surface))
+ SDL_LockSurface(screen_surface);
+ for (ptr = screen_surface->pixels,
+ tptr = &ptr[screen_width * screen_height];
+ ptr < tptr; ptr = &ptr[8]) {
+ ptr[0] = 0x00000000;
+ ptr[1] = 0x00000000;
+ ptr[2] = 0x00000000;
+ ptr[3] = 0x00000000;
+ ptr[4] = 0x00000000;
+ ptr[5] = 0x00000000;
+ ptr[6] = 0x00000000;
+ ptr[7] = 0x00000000;
+ }
+ if (SDL_MUSTLOCK(screen_surface))
+ SDL_UnlockSurface(screen_surface);
+ }
+ */
+ (void) SDL_FillRect(screen_surface, NULL, 0x00000000);
+
+ /* Update fps counter for stats */
+ fpscnt++;
+}
+
+void
+ship_draw(int id, int x, int y, double angle, uint8_t flags)
+{
+ char str[8];
+
+ (void) surface_blit_angle(rship, x, y, angle);
+ if ((flags & SHIPF_CLOAK) != 0)
+ filledCircleRGBA(screen_surface, x, y, 25,
+ 0x00, 0x00, 0x00, 0x80);
+ if ((flags & SHIPF_SHIELD) != 0) {
+ aacircleRGBA(screen_surface, x, y, 25,
+ 0xf0, 0xf0, 0x20, 0xb0);
+ filledCircleRGBA(screen_surface, x, y, 25,
+ 0x20, 0x50, 0xf0, 0x50);
+ }
+
+ x -= 24;
+ y -= 24;
+ (void) snprintf(str, 8, "%d", id);
+ font_blit_string(font, x, y, str, 0x00, 0x00, 0x00, 0xff);
+ font_blit_string(font, x + 1, y + 1, str, 0xff, 0xff, 0xff, 0xff);
+}
+
+void
+torp_draw(int x, int y, int r)
+{
+ static const uint8_t torp_colors[3][3] = {
+ { 0xff, 0xff, 0x00 }, /* 90% */
+ { 0xff, 0x30, 0x00 }, /* 5% */
+ { 0x00, 0x30, 0xff } /* 5% */
+ };
+ int rnd, c;
+
+ rnd = rand() % 100;
+ if (rnd < 5)
+ c = 2;
+ else if (rnd < 10)
+ c = 1;
+ else
+ c = 0;
+
+ filledCircleRGBA(screen_surface, x, y, 1 + (rand() % r),
+ torp_colors[c][0], torp_colors[c][1], torp_colors[c][2],
+ 0x7f + (rand() % 0x80));
+}
+
+static SDL_Surface *
+bmp_load_key(void *data, size_t size)
+{
+ SDL_RWops *rwo;
+ SDL_Surface *s;
+ Uint32 c;
+ void *ndata;
+ size_t nsize;
+
+ decode(&ndata, &nsize, data, size);
+
+ if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL)
+ goto err;
+ if ((s = IMG_LoadBMP_RW(rwo)) == NULL)
+ goto err;
+ SDL_FreeRW(rwo);
+
+ c = SDL_MapRGB(s->format, 0, 0, 0);
+ if (SDL_SetColorKey(s, SDL_SRCCOLORKEY | SDL_RLEACCEL, c) == -1)
+ goto err;
+
+ return s;
+
+err:
+ (void) fprintf(stderr, "bmp_load_key() - %s\n", SDL_GetError());
+ exit(EXIT_FAILURE);
+}
+
+static int
+surface_blit_angle(SDL_Surface *s, int x, int y, int a)
+{
+ SDL_Surface *t;
+ SDL_Rect d;
+ int r;
+
+ ASSERT(a > -1 && a < 256);
+
+ /*
+ * Convert 0-255 angle to 0-359 and add 90 degrees so that shapes
+ * bitmaps can point upwards, although angle 0 should point
+ * rightwards,
+ */
+ a = (a * 360 / 256) + 90;
+
+ /* Interestingly, the angle has to be reversed... */
+ if ((t = rotozoomSurface(s, -a, 1.0, 1)) != NULL) {
+ d = (SDL_Rect){x - (t->w / 2), y - (t->h / 2), 0, 0};
+ r = SDL_BlitSurface(t, NULL, screen_surface, &d);
+ SDL_FreeSurface(t);
+ } else
+ r = -1;
+
+ return r;
+}
+
+static Mix_Chunk *
+sample_load(void *data, size_t size)
+{
+ SDL_RWops *rwo;
+ Mix_Chunk *c;
+ void *ndata;
+ size_t nsize;
+
+ decode(&ndata, &nsize, data, size);
+
+ if ((rwo = SDL_RWFromMem(ndata, nsize)) == NULL)
+ goto err;
+
+ if ((c = Mix_LoadWAV_RW(rwo, 0)) == NULL)
+ goto err;
+
+ SDL_FreeRW(rwo);
+ return c;
+
+err:
+ (void) fprintf(stderr, "sample_load() - %s\n", SDL_GetError());
+ exit(EXIT_FAILURE);
+}
+
+static struct font *
+font_load(void *data, size_t size, int width, int height)
+{
+ struct font *font;
+ void *ndata;
+ size_t nsize;
+
+ decode(&ndata, &nsize, data, size);
+
+ if ((font = malloc(sizeof(struct font))) == NULL)
+ goto err;
+
+ font->data = ndata;
+ font->w = width;
+ font->h = height;
+ font->size = nsize;
+ return font;
+
+err:
+ (void) fprintf(stderr, "font_load()\n");
+ exit(EXIT_FAILURE);
+}
+
+static void
+font_blit_string(struct font *font, int x, int y, const char *str,
+ uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+
+ (void) gfxPrimitivesSetFont(font->data, font->w, font->h);
+ (void) stringRGBA(screen_surface, x, y, str, r, g, b, a);
+}
+
+/* ARGSUSED */
+static Uint32
+interval_callback(Uint32 interval, void *args)
+{
+
+ /* Calculate fps average in last 5 secs */
+ (void) sprintf(fpsstr, "%03d fps", fpscnt / 5);
+ fpscnt = 0;
+
+ /* Initiate ping */
+ pingticks = SDL_GetTicks();
+ cpacket_ping_send();
+
+ /* XXX Send faster updates request periodically */
+ cpacket_fast_send();
+
+ return interval;
+}
+
+/* Initialize trigonometric tables */
+static void
+trig_init(void)
+{
+ int i;
+ double f;
+
+ for (i = 0; i < 256; i++) {
+ f = ((2 * M_PI) / 256) * i;
+ sin_table[i] = sin(f);
+ cos_table[i] = cos(f);
+ }
+}
--- /dev/null
+/* $Id: main.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Exported resources by the main program, mainly for thread modules.
+ */
+
+
+
+#ifndef MAIN_H
+#define MAIN_H
+
+
+
+#include <SDL.h>
+
+#include <thread_msg.h>
+
+
+
+void frame_switch(void);
+void ship_draw(int, int, int, double, uint8_t);
+void torp_draw(int, int, int);
+
+
+
+extern thread_port_t main_port;
+
+extern Uint32 pingticks;
+extern char pingstr[8];
+
+extern char shipinfostr[64];
+
+
+
+#endif
--- /dev/null
+/* $Id: packets.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * To validate a received packet, we'll make sure that it's larger than
+ * sizeof(int), and that the first integer consists of a valid expected packet
+ * type ID within range. If so, we verify if packet length really corresponds
+ * to the packet type, and then can interpret the packet information.
+ * An actual network packet may contain a number of these packets.
+ * We thus must be able to efficiently determine each packet's length.
+ * Since we're using TCP, it should be enough. However, on the server side
+ * especially, proper sanity checking on input must be done to avoid crashing
+ * the server because of unexpected data processing.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <packets_common.h>
+#include <packets.h>
+#include <endian.h>
+#include <thread_net_send.h>
+#include <thread_msg.h>
+#include <debug.h>
+#include <hmac.h>
+#include <enc.h>
+#include <conf.h>
+
+
+
+static int spacket_auth_handler(uint16_t *);
+static int spacket_ping_handler(uint16_t *);
+static int spacket_eof_handler(uint16_t *);
+static int spacket_ship_handler(uint16_t *);
+static int spacket_shipinfo_handler(uint16_t *);
+static int spacket_torp_handler(uint16_t *);
+static int spacket_collision_handler(uint16_t *);
+static int svpacket_message_handler(uint16_t *);
+
+static int cpacket_send(const void *, size_t);
+#ifdef USE_ENCRYPTION
+static int cpacket_send_callback(const void *, size_t,
+ void (*)(void *), void *);
+static void cpacket_callback_encrypt(void *);
+#endif
+
+
+
+struct packet_index spacket_index[SVPACKET_MAX] = {
+ /* Fixed sized packets types */
+ {sizeof(struct spacket_auth), spacket_auth_handler},
+ {sizeof(struct spacket_ping), spacket_ping_handler},
+ {sizeof(struct spacket_pong), spacket_pong_handler},
+ {sizeof(struct spacket_eof), spacket_eof_handler},
+ {sizeof(struct spacket_ship), spacket_ship_handler},
+ {sizeof(struct spacket_shipinfo), spacket_shipinfo_handler},
+ {sizeof(struct spacket_torp), spacket_torp_handler},
+ {sizeof(struct spacket_collision), spacket_collision_handler},
+ /* Dynamic sized packets types */
+ {sizeof(struct svpacket_message), svpacket_message_handler}
+};
+
+
+
+/*
+ * There generally are multiple aligned packets into the supplied buffer.
+ * Process each of them calling their handler function.
+ */
+void
+spackets_handle(thread_amsg_t *amsg)
+{
+ uint8_t *data;
+ size_t size, off, len;
+
+ if (amsg->size == -1) {
+ (void) fprintf(stderr, "Error reading from server socket\n");
+ exit(EXIT_FAILURE);
+ }
+
+ data = (uint8_t *)amsg->data;
+ size = amsg->size;
+
+ for (off = 0; off < size; off += len) {
+ int8_t id;
+
+ /*
+ * Obtain packet type and evaluate its length.
+ * Because they are 16-bit aligned, make sure to
+ * increase len as necessary if it is odd.
+ */
+ id = (int8_t)data[off];
+ if (id >= SPACKET_MAX)
+ len = data[off + 1];
+ else
+ len = spacket_index[id].size;
+ if ((len & 1) != 0)
+ len++;
+
+ /* Kill client on packet processing error */
+ ASSERT(spacket_index[id].handler != NULL);
+ if (spacket_index[id].handler((uint16_t *)&data[off]) == -1) {
+ (void) fprintf(stderr,
+ "Error processing server packet 0x%02x\n", id);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static int
+spacket_auth_handler(uint16_t *ptr)
+{
+ struct spacket_auth *p = (struct spacket_auth *)ptr;
+
+ if (p->protocol_version != CLIENT_VERSION) {
+ (void) fprintf(stderr,
+ "Client and server versions do not match\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return cpacket_auth_send(CLIENT_LOGIN, CLIENT_PASSWD,
+ p->noncerand1, p->noncerand2);
+}
+
+/* ARGSUSED */
+static int
+spacket_ping_handler(uint16_t *ptr)
+{
+
+ /* Handled by the recvq code for better accuracy */
+ /* NOOP */
+
+ return 0;
+}
+
+/* Special since called by recvq.c */
+/* ARGSUSED */
+int
+spacket_pong_handler(uint16_t *ptr)
+{
+
+ (void) snprintf(pingstr, 8, "%04d ms", SDL_GetTicks() - pingticks);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+spacket_eof_handler(uint16_t *ptr)
+{
+
+ frame_switch();
+
+ return 0;
+}
+
+static int
+spacket_ship_handler(uint16_t *ptr)
+{
+ struct spacket_ship *p = (struct spacket_ship *)ptr;
+
+ p->id = BYTEORDER_HOST16(p->id);
+ p->x = BYTEORDER_HOST16(p->x);
+ p->y = BYTEORDER_HOST16(p->y);
+
+ ship_draw(p->id, p->x, p->y, p->angle, p->flags);
+
+ return 0;
+}
+
+static int
+spacket_shipinfo_handler(uint16_t *ptr)
+{
+ struct spacket_shipinfo *p = (struct spacket_shipinfo *)ptr;
+
+ p->fuel = BYTEORDER_HOST16(p->fuel);
+ p->shield = BYTEORDER_HOST16(p->shield);
+ p->hull = BYTEORDER_HOST16(p->hull);
+
+ (void) snprintf(shipinfostr, sizeof(shipinfostr),
+ "Thrust: %2d, Fuel: %5d, Shield: %3d, Hull: %3d",
+ p->thrust, p->fuel, p->shield, p->hull);
+
+ return 0;
+}
+
+static int
+spacket_torp_handler(uint16_t *ptr)
+{
+ struct spacket_torp *p = (struct spacket_torp *)ptr;
+
+ p->x = BYTEORDER_HOST16(p->x);
+ p->y = BYTEORDER_HOST16(p->y);
+
+ torp_draw(p->x, p->y, p->radius);
+
+ return 0;
+}
+
+static int
+spacket_collision_handler(uint16_t *ptr)
+{
+ /*struct spacket_collision *p = (struct spacket_collision *)ptr;*/
+
+ /* XXX */
+
+ return 0;
+}
+
+static int
+svpacket_message_handler(uint16_t *ptr)
+{
+ /*struct svpacket_message *p = (struct svpacket_message *)ptr;*/
+
+ /* XXX */
+
+ return 0;
+}
+
+/*
+ * Send asynchroneous message to the network sender thread with the data.
+ */
+static int
+cpacket_send(const void *buf, size_t size)
+{
+ thread_amsg_t *amsg;
+
+ if ((amsg = thread_amsg_create()) == NULL) {
+ (void) fprintf(stderr,
+ "cpacket_send() - thread_amsg_create()\n");
+ exit(EXIT_FAILURE);
+ }
+
+ (void) memcpy(amsg->data, buf, size);
+ amsg->size = size;
+ (void) thread_msg_put(&send_port, &amsg->msg);
+
+ return 0;
+}
+
+#ifdef USE_ENCRYPTION
+static int
+cpacket_send_callback(const void *buf, size_t size,
+ void (*callback)(void *), void *callback_arg)
+{
+ thread_amsg_t *amsg;
+
+ if ((amsg = thread_amsg_create()) == NULL) {
+ (void) fprintf(stderr,
+ "cpacket_send() - thread_amsg_create()\n");
+ exit(EXIT_FAILURE);
+ }
+
+ thread_amsg_setcallback(amsg, callback, callback_arg);
+ (void) memcpy(amsg->data, buf, size);
+ amsg->size = size;
+ (void) thread_msg_put(&send_port, &amsg->msg);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static void
+cpacket_callback_encrypt(void *arg)
+{
+
+ /* Enable encryption */
+ mmencrypt = 1;
+}
+#endif /* USE_ENCRYPTION */
+
+int
+cpacket_auth_send(const char *login, const char *passwd,
+ const uint8_t *noncerand1, const uint8_t *noncerand2)
+{
+ struct cpacket_auth p;
+ uint8_t hmac1[40], hmac2[40], ppasswd[32];
+ int ret;
+#ifdef USE_ENCRYPTION
+ uint8_t key[512];
+#endif
+
+ p.packet_type = CPACKET_AUTH;
+ p.protocol_version = CLIENT_VERSION;
+
+ /* Pad login string */
+ (void) memset(p.login, '\0', 16);
+ (void) strncpy((char *)p.login, login, 15);
+
+ /*
+ * Calculate sha1 and rmd160 hmac hashes for password and place first
+ * 80 bits of each into the hmac field. This is calculated using the
+ * nonce+random data previously obtained from the server. We do it
+ * for both noncerand.
+ */
+
+ /* Pad passwd */
+ (void) memset(ppasswd, '\0', 32);
+ (void) strncpy((char *)ppasswd, passwd, 31);
+
+ hmac_sha1(noncerand1, 32, ppasswd, 32, hmac1);
+ hmac_rmd160(noncerand1, 32, ppasswd, 32, &hmac1[20]);
+ (void) memcpy(p.hmac1, hmac1, 10);
+ (void) memcpy(&p.hmac1[10], &hmac1[20], 10);
+
+ hmac_sha1(noncerand2, 32, ppasswd, 32, hmac2);
+ hmac_rmd160(noncerand2, 32, ppasswd, 32, &hmac2[20]);
+ (void) memcpy(p.hmac2, hmac2, 10);
+ (void) memcpy(&p.hmac2[10], &hmac2[20], 10);
+
+#ifdef USE_ENCRYPTION
+ /*
+ * Use the full hmac results (320 bits) to create a session private
+ * key. This is done by using the hmac process again using the
+ * current full hmac against the user password. This generates a
+ * 320 bits key for use with the mmenc cipher, which is immediately
+ * enabled. We need to send our previous hmac back to the server
+ * under the new tunnel. This is done to create an input key with
+ * hmac1 and output one with hmac2.
+ */
+ hmac_sha1(hmac1, 40, ppasswd, 32, key);
+ hmac_rmd160(hmac1, 40, ppasswd, 32, &key[20]);
+ mmenc_init(&enc_in, key, 40);
+
+ hmac_sha1(hmac2, 40, ppasswd, 32, key);
+ hmac_rmd160(hmac2, 40, ppasswd, 32, &key[20]);
+ mmenc_init(&enc_out, key, 40);
+
+ (void) memset(key, '\0', 40);
+
+ ret = cpacket_send_callback(&p, sizeof(p),
+ cpacket_callback_encrypt, NULL);
+#else
+ ret = cpacket_send(&p, sizeof(p));
+#endif
+
+ (void) memset(hmac1, '\0', 40);
+ (void) memset(hmac2, '\0', 40);
+ (void) memset(ppasswd, '\0', 32);
+
+ return ret;
+}
+
+int
+cpacket_ping_send(void)
+{
+ struct cpacket_ping p;
+
+ p.packet_type = CPACKET_PING;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_pong_send(void)
+{
+ struct cpacket_pong p;
+
+ p.packet_type = CPACKET_PONG;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_direction_send(int angle)
+{
+ struct cpacket_direction p;
+
+ p.packet_type = CPACKET_DIRECTION;
+ p.angle = (uint8_t)angle;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_thrust_acc_send(void)
+{
+ struct cpacket_thrust_acc p;
+
+ p.packet_type = CPACKET_THRUST_ACC;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_thrust_dec_send(void)
+{
+ struct cpacket_thrust_dec p;
+
+ p.packet_type = CPACKET_THRUST_DEC;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_shield_send(void)
+{
+ struct cpacket_shield p;
+
+ p.packet_type = CPACKET_SHIELD;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_cloak_send(void)
+{
+ struct cpacket_cloak p;
+
+ p.packet_type = CPACKET_CLOAK;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_torp_send(int angle)
+{
+ struct cpacket_torp p;
+
+ p.packet_type = CPACKET_TORP;
+ p.angle = (uint8_t)angle;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_slow_send(void)
+{
+ struct cpacket_slow p;
+
+ p.packet_type = CPACKET_SLOW;
+
+ return cpacket_send(&p, sizeof(p));
+}
+
+int
+cpacket_fast_send(void)
+{
+ struct cpacket_fast p;
+
+ p.packet_type = CPACKET_FAST;
+
+ return cpacket_send(&p, sizeof(p));
+}
--- /dev/null
+/* $Id: packets.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _PACKETS_H_
+#define _PACKETS_H_
+
+
+
+#include <packets_common.h>
+#include <thread_msg.h>
+#include <main.h>
+
+
+
+/*
+ * For every server packet type we support, an index array will be used
+ * with this structure to call the associated handler function, which will
+ * perform sanity checking and process the packet, returning 0 on success or
+ * -1 on error. The size field will allow to perform sanity checking on
+ * data size prior to calling the handler function, as well as to increase the
+ * buffer pointer.
+ */
+struct packet_index {
+ size_t size;
+ int (*handler)(uint16_t *);
+};
+
+
+
+void spackets_handle(thread_amsg_t *);
+int spacket_pong_handler(uint16_t *);
+int cpacket_auth_send(const char *, const char *, const uint8_t *,
+ const uint8_t *);
+int cpacket_ping_send(void);
+int cpacket_pong_send(void);
+int cpacket_direction_send(int);
+int cpacket_thrust_acc_send(void);
+int cpacket_thrust_dec_send(void);
+int cpacket_shield_send(void);
+int cpacket_cloak_send(void);
+int cpacket_torp_send(int);
+int cpacket_slow_send(void);
+int cpacket_fast_send(void);
+
+
+
+extern struct packet_index spacket_index[SVPACKET_MAX];
+
+
+
+#endif
--- /dev/null
+/* $Id: pool.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2001-2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dlist.h>
+#include <pool.h>
+#include <debug.h>
+
+
+
+/* DEFINITIONS */
+
+#define BPAGE_SIZE ((size_t)OALIGN_CEIL(sizeof(bpage_t), long))
+
+
+
+/* STATIC FUNCTION PROTOTYPES */
+
+static bpage_t * pool_page_create(const char *, pool_t *);
+static bpage_t * pool_page_destroy(bpage_t *);
+
+
+
+/* STATIC FUNCTIONS */
+
+/*
+ * Creates a new page of objects, and calls the constructor function for each
+ * object of the new page if needed. Returns NULL on failure, or the new
+ * bpage_t pointer on success.
+ */
+static bpage_t *
+pool_page_create(const char *func, pool_t *pool)
+{
+ register size_t nodesize = pool->nodesize;
+ register uint8_t *ptr, *toptr;
+ register bpage_t *page;
+ register list_t *list;
+
+ if ((page = pool->malloc(pool->pagesize)) == NULL)
+ return NULL;
+
+ /* Initialize bpage_t */
+ page->magic = MAGIC_PAGE;
+ page->pool = pool;
+ DLIST_INIT(&page->objects);
+
+ /*
+ * Create all objects of that bpage_t, inserting them into it's list_t.
+ * If any object creation fails (it's optional construtor function can
+ * fail), destroy the page and return NULL.
+ */
+ if (pool->create != NULL) {
+ for (ptr = toptr = (uint8_t *)page, ptr += BPAGE_SIZE,
+ toptr += pool->pagesize, list = &page->objects;
+ ptr + nodesize < toptr; ptr += nodesize) {
+ ((pnode_t *)ptr)->magic = 0;
+ ((pnode_t *)ptr)->page = page;
+ if (pool->create((pnode_t *)ptr) != 0)
+ return pool_page_destroy(page);
+ DLIST_APPEND(list, (node_t *)ptr);
+ }
+ } else {
+ for (ptr = toptr = (uint8_t *)page, ptr += BPAGE_SIZE,
+ toptr += pool->pagesize, list = &page->objects;
+ ptr + nodesize < toptr; ptr += nodesize) {
+ ((pnode_t *)ptr)->magic = 0;
+ ((pnode_t *)ptr)->page = page;
+ DLIST_APPEND(list, (node_t *)ptr);
+ }
+ }
+
+ return page;
+}
+
+/*
+ * Destroys a page previously created using pool_page_create(), calling the
+ * destructor function for each object of the page if necessary.
+ * Returns NULL.
+ */
+static bpage_t *
+pool_page_destroy(bpage_t *page)
+{
+ register pnode_t *pnode;
+ register void (*destroy)(pnode_t *);
+
+ if ((destroy = (page->pool->destroy)) != NULL) {
+ /* We need to destroy all objects */
+ DLIST_FOREACH(&page->objects, pnode)
+ destroy(pnode);
+ }
+
+ page->pool->free(page);
+
+ return NULL;
+}
+
+
+
+/* PUBLIC FUNCTIONS */
+
+/*
+ * Initializes a memory pool for efficient management of fixed sized nodes.
+ * Returns 0 on success or -1 on failure.
+ */
+int
+pool_init(pool_t *pool, const char *label, void *(*mallocfunc)(size_t),
+ void (*freefunc)(void *), int (*create)(pnode_t *),
+ void (*destroy)(pnode_t *), size_t nodesize, uint32_t nodesperpage,
+ uint32_t minpages, uint32_t maxpages)
+{
+ int ok = -1;
+ register size_t ns, ps;
+
+ ASSERT(!POOL_VALID(pool));
+ ASSERT(pool != NULL && mallocfunc != NULL && freefunc != NULL &&
+ ((create != NULL && destroy != NULL) ||
+ (void *)create == (void *)destroy) &&
+ nodesize != 0 && nodesperpage > 0);
+
+ ns = (size_t)OALIGN_CEIL(nodesize, long);
+ ps = (BPAGE_SIZE + ((nodesperpage + 1) * ns));
+
+ pool->magic = 0;
+ pool->label = label;
+ pool->malloc = mallocfunc;
+ pool->free = freefunc;
+ pool->create = create;
+ pool->destroy = destroy;
+ pool->nodesize = ns;
+ pool->minpages = minpages;
+ pool->maxpages = maxpages;
+ pool->nodesperpage = nodesperpage;
+ pool->pagesize = ps;
+ pool->avgtotal = pool->avgcnt = minpages;
+ DLIST_INIT(&pool->pages);
+ DLIST_INIT(&pool->fpages);
+ DLIST_INIT(&pool->epages);
+
+ /*
+ * Allocate minimum number of pages, if needed. We insert them into
+ * the empty pages list, but minpages will be honored as such to
+ * never free pages below it.
+ */
+ for (; minpages > 0; minpages--) {
+ register bpage_t *p;
+
+ if ((p = pool_page_create("pool_init", pool)) == NULL)
+ break;
+ DLIST_APPEND(&pool->epages, (node_t *)p);
+ }
+
+ /* Validate this pool */
+ pool->magic = MAGIC_POOL;
+
+ /*
+ * Have we failed to allocate any needed pages? If so, destroy pool
+ * and return -1.
+ */
+ if (minpages == 0)
+ ok = 0;
+ else if (minpages < pool->minpages)
+ (void) pool_destroy(pool);
+
+ return ok;
+}
+
+
+/*
+ * Destroys a pool which previously has been created by pool_init(), and frees
+ * any resources it uses (all the nodes it created become invalid).
+ */
+void
+pool_destroy(pool_t *pool)
+{
+ register bpage_t *p, *t;
+
+ ASSERT(POOL_VALID(pool));
+
+ /* Destroy all pages of all lists */
+ for (p = DLIST_TOP(&pool->pages); p != NULL; p = t) {
+ t = DLIST_NEXT(p);
+ (void) pool_page_destroy(p);
+ }
+ for (p = DLIST_TOP(&pool->fpages); p != NULL; p = t) {
+ t = DLIST_NEXT(p);
+ (void) pool_page_destroy(p);
+ }
+ for (p = DLIST_TOP(&pool->epages); p != NULL; p = t) {
+ t = DLIST_NEXT(p);
+ (void) pool_page_destroy(p);
+ }
+
+ /* Invalidate pool_t */
+ pool->magic = 0;
+}
+
+
+/*
+ * Allows to very efficiently allocate a single node from the specified pool,
+ * optionally initializing it's memory to zeros. Returns the newly allocated
+ * node pointer on success, or NULL on error.
+ */
+pnode_t *
+pool_alloc(pool_t *pool, int zero)
+{
+ pnode_t *pnode = NULL;
+ register bpage_t *page;
+
+ ASSERT(POOL_VALID(pool));
+ ASSERT(!zero || pool->create == NULL);
+
+ /* Are there any partially used pages? */
+ if ((page = DLIST_TOP(&pool->pages)) == NULL) {
+ /*
+ * No, we thus attempt to move a page from our empty pages
+ * cache back into our usable pages list. The order of the
+ * usable page list becomes irrelevant at DLIST_SWAP() since
+ * it will be the only node.
+ */
+ if ((page = DLIST_TOP(&pool->epages)) == NULL) {
+ /*
+ * No more free pages in our cache neither.
+ * If maxpages is set and not yet reached, we need
+ * to create a new page. If we can't, return NULL
+ * for error.
+ */
+ if (pool->maxpages == 0 ||
+ DLIST_NODES(&pool->fpages) < pool->maxpages) {
+ if ((page = pool_page_create("pool_alloc",
+ pool)) == NULL)
+ return NULL;
+ DLIST_APPEND(&pool->pages, (node_t *)page);
+ } else
+ return NULL;
+ } else
+ DLIST_SWAP(&pool->pages, &pool->epages, (node_t *)page,
+ 0);
+ }
+
+ /*
+ * <page> now points to a page we know we can at least get a free
+ * object node from. Obtain one and unlink it.
+ */
+ pnode = DLIST_TOP(&page->objects);
+ DLIST_UNLINK(&page->objects, (node_t *)pnode);
+
+ /*
+ * Have we obtained the last available object from this page?
+ * If so, move the page to the full pages list. The order of the
+ * full pages list nodes is irrelevant, since we don't know which
+ * of those pages are more likely to be swapped to the usable
+ * pages list first, and we won't run through that list, except at
+ * pool_destroy().
+ */
+ if (DLIST_NODES(&page->objects) == 0)
+ DLIST_SWAP(&pool->fpages, &pool->pages, (node_t *)page, 0);
+
+ /*
+ * If requested, zero object. This is stupid if a constructor and
+ * destructor are used, but can be useful otherwise.
+ */
+ if (zero) {
+ page = pnode->page;
+ (void) memset(pnode, 0, pool->nodesize);
+ pnode->page = page;
+ }
+
+ /* Mark this node as a valid allocated object */
+ pnode->magic = MAGIC_PNODE;
+
+ return pnode;
+}
+
+
+/*
+ * Efficiently frees the specified node back to the pool it was allocated from.
+ * Returns NULL. Uses heuristics keeping statistics to determine when to
+ * actually shrink the memory blocks internally used by the pool, so that
+ * frequently growing and shrinking pools will remain large for scalability.
+ * This also makes a big difference when constructors and destructors are used
+ * and need to execute a significant amount of code.
+ */
+pnode_t *
+pool_free(pnode_t *pnode)
+{
+ register bpage_t *page = pnode->page;
+ register pool_t *pool = page->pool;
+ register uint32_t count;
+
+ ASSERT(PNODE_VALID(pnode));
+
+ /* Invalidate object */
+ pnode->magic = 0;
+
+ /*
+ * Record how many nodes there currently are in our page's object
+ * list, to be used later on
+ */
+ count = DLIST_NODES(&page->objects);
+
+ /*
+ * Add node back to it's page's object list. Insert it so that we
+ * favor reuse of recently used objects soon.
+ */
+ DLIST_INSERT(&page->objects, (node_t *)pnode);
+
+ /*
+ * Was this page full before we inserted our node? If so, page is in
+ * full pages list. Move it to the usable pages list. Insert it to
+ * favor reuse of recently used pages soon (this page will soon return
+ * back to the full pages list).
+ */
+ if (count == 0)
+ DLIST_SWAP(&pool->pages, &pool->fpages, (node_t *)page, 1);
+ else {
+ /*
+ * Did we cause our node insertion to totally free back the
+ * page? If so, the page is on the usable free list, but move
+ * it back to the empty pages list. Insert it to favor reuse
+ * of recently used pages soon (this page will be the first to
+ * get used again when the usable pages list needs pages).
+ */
+ if (++count == pool->nodesperpage) {
+ DLIST_SWAP(&pool->epages, &pool->pages, (node_t *)page,
+ 1);
+ }
+ }
+
+ if ((pool->minpages < pool->maxpages) ||
+ (pool->minpages == 0 && pool->maxpages == 0)) {
+ register int exceeding;
+
+ /*
+ * This pool_t is allowed to shrink. Maintain average pages
+ * usage to prevent destroying our pages in the empty pages
+ * list unless we should.
+ */
+ count = DLIST_NODES(&pool->pages) + DLIST_NODES(&pool->fpages);
+ pool->avgtotal += count;
+ pool->avgcnt++;
+
+ /*
+ * Using * 8 here means that pool_free() needs to at least be
+ * called to release as much objects as can fit into eight full
+ * pages in order to execute the following block. But of
+ * course, this does not mean that eight pages will recently
+ * have been freed, since allocations probably also occured
+ * meanwhile.
+ */
+ if (pool->avgcnt > pool->nodesperpage * 8) {
+ /*
+ * Do statistics suggest that we should shrink the
+ * pool? If so, free pages from our empty pages
+ * cache back to the system, destroying their objects
+ * if necessary. We'll make sure to at least leave a
+ * one page hysterisis for better performance.
+ */
+ if ((exceeding = (count + DLIST_NODES(&pool->epages)
+ - 1) - (pool->avgtotal / pool->avgcnt)) > 0) {
+ register list_t *epages = &pool->epages;
+
+ /*
+ * Preferably free pages which haven't been
+ * used recently.
+ */
+ for (; exceeding > 0 &&
+ (page = DLIST_BOTTOM(epages)) != NULL;
+ exceeding--) {
+ DLIST_UNLINK(epages, (node_t *)page);
+ (void) pool_page_destroy(page);
+ }
+ }
+ /* Reset statistics */
+ pool->avgcnt = 1;
+ pool->avgtotal = count;
+ }
+ }
+
+ return NULL;
+}
--- /dev/null
+/* $Id: pool.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2001-2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+
+#ifndef MPOOL_H
+#define MPOOL_H
+
+
+
+#include <stdint.h>
+
+#include <dlist.h>
+
+
+
+/* Useful to o-align a value relative to v (sizes and pointers) */
+#define OALIGN_CEIL(v, o) \
+ ((((size_t)(v)) + (sizeof(o)) - 1) / (sizeof(o)) * (sizeof(o)))
+#define OALIGN_FLOOR(v, o) ((size_t)(v) - (((size_t)(v) % sizeof(o))))
+
+/* Useful to byte align a value with supplied size */
+#define BALIGN_CEIL(v, s) ((((size_t)(v)) + (s) - 1) / (s) * (s))
+#define BALIGN_FLOOR(v, s) ((size_t)(v) - (((size_t)(v) % (s))))
+
+
+
+typedef struct pnode pnode_t;
+typedef struct bpage bpage_t;
+typedef struct pool pool_t;
+
+
+
+#define MAGIC_POOL 0x504f4f4c /* POOL */
+#define MAGIC_PAGE 0x50414745 /* PAGE */
+#define MAGIC_PNODE 0x504e4f44 /* PNOD */
+
+#define POOL_VALID(p) ((p) != NULL && (p)->magic == MAGIC_POOL)
+#define PNODE_VALID(p) ((p) != NULL && (p)->magic == MAGIC_PNODE && \
+ (p)->page != NULL && (p)->page->magic == MAGIC_PAGE && \
+ (p)->page->pool != NULL && (p)->page->pool->magic == MAGIC_POOL)
+
+
+
+/* Header structure of internally allocated/prepared page/blocks */
+struct bpage {
+ node_t node;
+ uint32_t magic;
+ pool_t *pool;
+ list_t objects;
+};
+
+/* Header structure of individual pool objects */
+struct pnode {
+ node_t node;
+ uint32_t magic;
+ bpage_t *page;
+};
+
+/* Pool control structure */
+struct pool {
+ pnode_t node; /* Hey, we never know, a pool_t of pool_t */
+ uint32_t magic;
+ const char *label;
+ void *(*malloc)(size_t);
+ void (*free)(void *);
+ int (*create)(pnode_t *);
+ void (*destroy)(pnode_t *);
+ size_t nodesize, pagesize;
+ uint32_t minpages, maxpages, nodesperpage;
+ uint32_t avgtotal, avgcnt;
+ /* Usable pages, totally full/busy pages, totally empty/free pages */
+ list_t pages, fpages, epages;
+};
+
+
+
+/* Public API prototypes */
+extern int pool_init(pool_t *, const char *, void *(*)(size_t),
+ void (*)(void *), int (*)(pnode_t *),
+ void (*)(pnode_t *), size_t,
+ uint32_t, uint32_t, uint32_t);
+extern void pool_destroy(pool_t *);
+extern pnode_t * pool_alloc(pool_t *, int);
+extern pnode_t * pool_free(pnode_t *);
+
+
+
+#endif
--- /dev/null
+/* $Id: rawobjs.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/* XXX This file should probably be auto-generated */
+
+
+
+#ifndef RAWOBJS_H
+#define RAWOBJS_H
+
+
+
+/*
+ * On win32, binary objects must be referenced without the underscore prefix.
+ * Let's then define a few aliases.
+ */
+#ifdef WIN32
+
+#define _binary_bmp_FedCA_bmp_enc_size binary_bmp_FedCA_bmp_enc_size
+#define _binary_bmp_FedCA_bmp_enc_start binary_bmp_FedCA_bmp_enc_start
+#define _binary_bmp_RomDD_bmp_enc_size binary_bmp_RomDD_bmp_enc_size
+#define _binary_bmp_RomDD_bmp_enc_start binary_bmp_RomDD_bmp_enc_start
+
+#define _binary_wav_nt_cloaked_wav_enc_size binary_wav_nt_cloaked_wav_enc_size
+#define _binary_wav_nt_cloaked_wav_enc_start binary_wav_nt_cloaked_wav_enc_start
+#define _binary_wav_nt_explosion_other_wav_enc_size binary_wav_nt_explosion_other_wav_enc_size
+#define _binary_wav_nt_explosion_other_wav_enc_start binary_wav_nt_explosion_other_wav_enc_start
+#define _binary_wav_nt_fire_torp_other_wav_enc_size binary_wav_nt_fire_torp_other_wav_enc_size
+#define _binary_wav_nt_fire_torp_other_wav_enc_start binary_wav_nt_fire_torp_other_wav_enc_start
+#define _binary_wav_nt_plasma_hit_wav_enc_size binary_wav_nt_plasma_hit_wav_enc_size
+#define _binary_wav_nt_plasma_hit_wav_enc_start binary_wav_nt_plasma_hit_wav_enc_start
+#define _binary_wav_nt_shield_down_wav_enc_size binary_wav_nt_shield_down_wav_enc_size
+#define _binary_wav_nt_shield_down_wav_enc_start binary_wav_nt_shield_down_wav_enc_start
+#define _binary_wav_nt_shield_up_wav_enc_size binary_wav_nt_shield_up_wav_enc_size
+#define _binary_wav_nt_shield_up_wav_enc_start binary_wav_nt_shield_up_wav_enc_start
+#define _binary_wav_nt_uncloak_wav_enc_size binary_wav_nt_uncloak_wav_enc_size
+#define _binary_wav_nt_uncloak_wav_enc_start binary_wav_nt_uncloak_wav_enc_start
+
+#define _binary_fnt_7x13_fnt_enc_start binary_fnt_7x13_fnt_enc_start
+#define _binary_fnt_7x13_fnt_enc_size binary_fnt_7x13_fnt_enc_size
+
+#endif
+
+
+
+/*
+ * And export symbols to other modules
+ */
+
+extern void *_binary_bmp_FedCA_bmp_enc_size;
+extern void *_binary_bmp_FedCA_bmp_enc_start;
+extern void *_binary_bmp_RomDD_bmp_enc_size;
+extern void *_binary_bmp_RomDD_bmp_enc_start;
+
+extern void *_binary_wav_nt_cloaked_wav_enc_size;
+extern void *_binary_wav_nt_cloaked_wav_enc_start;
+extern void *_binary_wav_nt_explosion_other_wav_enc_size;
+extern void *_binary_wav_nt_explosion_other_wav_enc_start;
+extern void *_binary_wav_nt_fire_torp_other_wav_enc_size;
+extern void *_binary_wav_nt_fire_torp_other_wav_enc_start;
+extern void *_binary_wav_nt_plasma_hit_wav_enc_size;
+extern void *_binary_wav_nt_plasma_hit_wav_enc_start;
+extern void *_binary_wav_nt_shield_down_wav_enc_size;
+extern void *_binary_wav_nt_shield_down_wav_enc_start;
+extern void *_binary_wav_nt_shield_up_wav_enc_size;
+extern void *_binary_wav_nt_shield_up_wav_enc_start;
+extern void *_binary_wav_nt_uncloak_wav_enc_size;
+extern void *_binary_wav_nt_uncloak_wav_enc_start;
+
+extern void *_binary_fnt_7x13_fnt_enc_start;
+extern void *_binary_fnt_7x13_fnt_enc_size;
+
+
+
+#endif
--- /dev/null
+/* $Id: recvq.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <SDL_net.h>
+
+#include <conf.h>
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+#include <recvq.h>
+#include <packets.h>
+#ifdef USE_ENCRYPTION
+#include <enc.h>
+#endif
+
+
+
+#define RBUFFER_SIZE 4096
+#ifdef USE_COMPRESSION
+#define IBUFFER_SIZE (RBUFFER_SIZE * 64)
+#endif
+
+
+
+static int8_t rbuffer[RBUFFER_SIZE];
+#ifdef USE_COMPRESSION
+static char ibuffer[IBUFFER_SIZE];
+#endif
+
+
+
+int
+recvq_init(recvq_t *q, TCPsocket sock, size_t size)
+{
+
+ if ((size & 1) != 0)
+ size++;
+
+ if ((q->buffer = malloc(size)) != NULL) {
+#ifdef USE_COMPRESSION
+ q->zin.next_in = q->zin.next_out = NULL;
+ q->zin.avail_in = q->zin.avail_out = 0;
+ q->zin.zalloc = NULL;
+ q->zin.zfree = NULL;
+ q->zin.opaque = NULL;
+ if (inflateInit(&q->zin) == Z_OK) {
+#endif
+ q->sock = sock;
+ q->size = size;
+ q->tail = q->ptail = 0;
+
+ q->variable = q->last = -1;
+ q->length = -1;
+
+ return 0;
+#ifdef USE_COMPRESSION
+ } else
+ free(q->buffer);
+#endif
+ }
+
+ return -1;
+}
+
+void
+recvq_destroy(recvq_t *q)
+{
+
+ free(q->buffer);
+#ifdef USE_COMPRESSION
+ (void) inflateEnd(&q->zin);
+#endif
+}
+
+/*
+ * Reads up to RBUFFER_SIZE of ready data from the socket and fills the
+ * received packets queue buffer. Buffered packets are 16-bit aligned with 0
+ * padding. Minimizes calls to SDLNet_TCP_Recv by using a buffer and
+ * recopying it instead of reading one byte at a time for the packet type
+ * and length.
+ * Automatically proceses SPACKET_PING for accurate latency evaluation,
+ * which packets will not be queued.
+ * Returns 0 on success, or -1 on error or 1 when an End Of Frame packet
+ * was just received.
+ */
+int
+recvq_read(recvq_t *q)
+{
+ int s, eof;
+ int8_t *ptr, *tptr, *buf;
+#ifdef USE_COMPRESSION
+ int ret;
+#endif
+
+ /*
+ * First read available data in an unaligned buffer, which may then
+ * begin and end by partial packets and contain multiple unaligned
+ * packets.
+ */
+ if ((s = SDLNet_TCP_Recv(q->sock, rbuffer, RBUFFER_SIZE)) == -1 ||
+ s == 0)
+ return -1;
+
+#ifdef USE_ENCRYPTION
+ /* Decrypt incomming data */
+ if (mmencrypt)
+ mmenc_decrypt(&enc_in, (uint8_t *)rbuffer, s);
+#endif
+
+ /* Decompress incomming data */
+#ifdef USE_COMPRESSION
+ q->zin.next_in = (Bytef *)rbuffer;
+ q->zin.avail_in = s;
+ q->zin.next_out = (Bytef *)ibuffer;
+ q->zin.avail_out = IBUFFER_SIZE;
+ if ((ret = inflate(&q->zin, Z_SYNC_FLUSH)) != Z_OK &&
+ ret != Z_STREAM_END)
+ return -1;
+ buf = (int8_t *)ibuffer;
+ s = IBUFFER_SIZE - q->zin.avail_out;
+#else
+ buf = rbuffer;
+#endif /* USE_COMPRESSION */
+
+ /*
+ * Process previous raw buffer and queue aligned full packets.
+ * We take care to verify recvq boundaries when adding even bytes,
+ * we know that the buffer is 16-bit aligned.
+ */
+ eof = 0;
+ for (ptr = buf, tptr = &buf[s]; ptr < tptr; ) {
+ if (q->length != -1) {
+ /* Still reading incomplete packet */
+
+ if (q->length > 0) {
+ /* Byte may be odd or even check recvq limit */
+ if (q->tail == q->size) {
+ (void) fprintf(stderr,
+ "recv_read() - recvq full\n");
+ return -1;
+ }
+ q->buffer[q->tail++] = *ptr++;
+ q->length--;
+ }
+ if (q->length == 0) {
+complete:
+ /* Packet complete, align buffer for next */
+ q->length = -1;
+ if ((q->tail & 1) != 0)
+ q->buffer[q->tail++] = 0;
+
+ /*
+ * Immediately process ping request packets
+ * since we must properly permit to measure
+ * the network latency. We also dequeue ping
+ * packets transparently.
+ * We only allow one ping request from server
+ * heartbeat cycle, and we only allow them if
+ * the client is already authenticated.
+ * Note that server pong packets are sent
+ * directly rather than passing through the
+ * sendq, unlike other packets.
+ */
+ if (q->last == SPACKET_PING) {
+ /* Discard packet from queue */
+ q->tail -= 2;
+ if (cpacket_pong_send() == -1) {
+ (void) fprintf(stderr,
+ "recvq_read() - "
+ "spacket_pong_send()\n");
+ return -1;
+ }
+ continue;
+ }
+
+ /* Same with incomming pong replies */
+ if (q->last == SPACKET_PONG) {
+ q->tail -= 2;
+ (void) spacket_pong_handler(NULL);
+ continue;
+ }
+
+ /*
+ * If we received an End Of Frame packet,
+ * we must be sure to return with 1 on
+ * success.
+ */
+ eof = 1;
+
+ /*
+ * Update the completed packets tail pointer,
+ * ptail.
+ */
+ q->ptail = q->tail;
+
+ continue;
+ }
+ } else {
+ /*
+ * New packet. We must evaluate packet length by
+ * packet type, and take into consideration variable
+ * packets which length will be provided as a second
+ * byte. For variable packets, we must make sure that
+ * the received length is at least high enough for the
+ * minimum length of the packet.
+ */
+ if (q->variable != -1) {
+ /* Receiving length of variable packet */
+ q->length = (ssize_t)((uint8_t)*ptr);
+ if (q->length <
+ spacket_index[q->variable].size) {
+ (void) fprintf(stderr,
+ "recvq_read() - "
+ "Invalid packet size %d\n",
+ (int)q->length);
+ q->variable = -1;
+ q->length = -1;
+ return -1;
+ }
+ q->variable = -1;
+
+ /* Odd */
+ q->buffer[q->tail++] = *ptr++;
+ q->length--;
+ } else {
+ int8_t t;
+
+ /* Receiving type of packet. Even */
+ if (q->tail == q->size) {
+ (void) fprintf(stderr,
+ "recv_read() - recvq full\n");
+ return -1;
+ }
+ t = (q->buffer[q->tail++] = *ptr++);
+ q->last = t;
+
+ if (t < 0 || t > SVPACKET_MAX) {
+ (void) fprintf(stderr,
+ "recv_read() - "
+ "Invalid packet type 0x%02x\n",
+ (uint8_t)t);
+ return -1;
+ }
+ if (t < SPACKET_MAX) {
+ /* Fixed packet */
+ q->length = spacket_index[t].size - 1;
+ q->variable = -1;
+ /*
+ * Special provision for single byte
+ * packets
+ */
+ if (q->length == 0)
+ goto complete;
+ } else {
+ /* Variable packet, awaiting size */
+ q->length = -1;
+ q->variable = t;
+ }
+ }
+ }
+ }
+
+ if (eof)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Returns a pointer to the starting point of the buffer and the size of the
+ * buffer. May contain multiple contiguous 16-bit aligned packets with
+ * padding. Only full packets are returned.
+ */
+void
+recvq_content(recvq_t *q, uint8_t **buf, size_t *size)
+{
+
+ *buf = (uint8_t *)q->buffer;
+ *size = q->ptail;
+ if ((*size & 1) != 0)
+ (*size)++;
+}
+
+/*
+ * To be called after recvq_content() supplied buffer is processed.
+ * Repositions offsets while making sure to preserve any previously incomplete
+ * packets.
+ */
+void
+recvq_content_reset(recvq_t *q)
+{
+
+ if (q->ptail == 0)
+ return;
+
+ if (q->tail > q->ptail) {
+ register size_t s;
+
+ s = q->tail - q->ptail;
+ (void) memmove(q->buffer, &q->buffer[q->ptail], s);
+ q->tail = s;
+ } else
+ q->tail = 0;
+
+ q->ptail = 0;
+}
--- /dev/null
+/* $Id: recvq.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _RECVQ_H_
+#define _RECVQ_H_
+
+
+
+#include <stdint.h>
+
+#include <SDL_net.h>
+#include <zlib.h>
+
+#include <mmenc.h>
+
+
+
+typedef struct recvq {
+ TCPsocket sock;
+ uint8_t *buffer;
+ size_t size, tail, ptail;
+ /* Necessary for fragmented packets reassembly */
+ int variable, last;
+ ssize_t length;
+ z_stream zin;
+} recvq_t;
+
+
+
+int recvq_init(recvq_t *, TCPsocket sock, size_t);
+void recvq_destroy(recvq_t *);
+int recvq_read(recvq_t *);
+void recvq_content(recvq_t *, uint8_t **, size_t *);
+void recvq_content_reset(recvq_t *);
+
+
+
+#endif
--- /dev/null
+/* $Id: screen.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Display related initialization.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <SDL.h>
+
+#include <conf.h>
+#include <screen.h>
+
+
+
+#define SCREEN_WIDTH 640
+#define SCREEN_HEIGHT 480
+
+
+
+SDL_Surface *screen_surface;
+int screen_width, screen_height;
+SDL_Joystick *gamepad;
+
+
+
+void
+screen_init(void)
+{
+
+ if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
+ (void) fprintf(stderr, "main() - SDL_Init() - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if ((screen_surface = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32,
+ SDL_DOUBLEBUF/* | SDL_FULLSCREEN*/)) == NULL) {
+ (void) fprintf(stderr, "main() - SDL_SetVideoMode() - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ if (SDL_NumJoysticks > 0)
+ gamepad = SDL_JoystickOpen(0);
+
+ screen_width = SCREEN_WIDTH;
+ screen_height = SCREEN_HEIGHT;
+
+ (void) atexit(screen_destroy);
+}
+
+void
+screen_destroy(void)
+{
+
+ SDL_Quit();
+}
--- /dev/null
+/* $Id: screen.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Screen related initialization.
+ */
+
+
+
+#ifndef SCREEN_H
+#define SCREEN_H
+
+
+
+#include <SDL.h>
+
+
+
+extern SDL_Surface *screen_surface;
+extern int screen_width, screen_height;
+extern SDL_Joystick *gamepad;
+
+
+
+void screen_init(void);
+void screen_destroy(void);
+
+
+
+#endif
--- /dev/null
+/* $Id: thread_msg.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Adapted from older code intended for use with POSIX threads,
+ * for use within SDL.
+ */
+
+
+
+#include <stdlib.h>
+
+#include <debug.h>
+#include <thread_msg.h>
+#include <pool.h>
+
+
+
+static int amsg_create(pnode_t *);
+static void amsg_destroy(pnode_t *);
+
+
+
+static pool_t amsg_pool;
+static SDL_mutex *amsg_mutex;
+static int amsg_initialized = 0;
+
+
+
+/*
+ * Allows to initialize a polling notification handle. When attached to a
+ * port, a message arriving on an empty port causes the associated ring to
+ * wake the thread from thread_ring_wait().
+ */
+int
+thread_ring_init(thread_ring_t *ring)
+{
+
+ ASSERT(ring != NULL && ring->magic != PRING_MAGIC);
+
+ if ((ring->cond = SDL_CreateCond()) != NULL) {
+ if ((ring->mutex = SDL_CreateMutex()) != NULL) {
+ ring->event = 0;
+ ring->magic = PRING_MAGIC;
+
+ return 0;
+ }
+ SDL_DestroyCond(ring->cond);
+ }
+
+ return -1;
+}
+
+/*
+ * Returns TRUE if the supplied ring is a valid/usable one, or FALSE
+ * otherwise. Useful to conditionally destroy it.
+ */
+int
+thread_ring_valid(thread_ring_t *ring)
+{
+
+ ASSERT(ring != NULL);
+
+ return (ring != NULL && ring->magic == PRING_MAGIC);
+}
+
+/*
+ * Destroys a ring. Note that all message ports attached to this ring should
+ * first be detached or destroyed.
+ */
+void
+thread_ring_destroy(thread_ring_t *ring)
+{
+
+ ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
+
+ SDL_DestroyMutex(ring->mutex);
+ SDL_DestroyCond(ring->cond);
+ ring->magic = 0;
+}
+
+/*
+ * Causes the current thread to sleep until a message arrives on an empty port
+ * associated with this ring. In normal operation, a thread only goes in wait
+ * mode after it processed all queued messages on all interesting ports.
+ * The ring is only notified when the port is empty and that a message is
+ * queued into it.
+ */
+int
+thread_ring_wait(thread_ring_t *ring, Uint32 ms)
+{
+ int error = 0;
+
+ ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
+
+ /* We must hold the condition variable's mutex */
+ if (SDL_LockMutex(ring->mutex) != 0) {
+ error = -1;
+ goto err;
+ }
+
+ /* As long as we don't have confirmation that we must stop waiting */
+ for (ring->event = 0; !ring->event && error == 0; ) {
+ /*
+ * Wait on conditional variable, which will automatically
+ * and atomically release the mutex and return with the mutex
+ * locked again, as soon as the conditional variable gets
+ * signaled.
+ */
+ if (ms != 0)
+ error = SDL_CondWaitTimeout(ring->cond, ring->mutex,
+ ms);
+ else
+ error = SDL_CondWait(ring->cond, ring->mutex);
+ }
+
+ /*
+ * And we know that conditional waiting functions returned with mutex
+ * locked, so now release it back.
+ */
+ (void) SDL_UnlockMutex(ring->mutex);
+
+err:
+ return error;
+}
+
+/*
+ * Allows to wake up waiter(s) on the specified ring, which are sleeping
+ * threads within thread_ring_wait(). This can be used to simulate the
+ * arrival of a message on an empty port. Also useful to use rings as a
+ * notification system only when no message passing is needed.
+ */
+int
+thread_ring_notify(thread_ring_t *ring)
+{
+
+ ASSERT(ring != NULL && ring->magic == PRING_MAGIC);
+
+ if (SDL_LockMutex(ring->mutex) != 0)
+ return -1;
+
+ ring->event = 1;
+ (void) SDL_CondSignal(ring->cond);
+
+ (void) SDL_UnlockMutex(ring->mutex);
+
+ return 0;
+}
+
+/*
+ * Allows to initialize/create a message port.
+ */
+int
+thread_port_init(thread_port_t *port)
+{
+
+ ASSERT(port != NULL && port->magic != PPORT_MAGIC);
+
+ if ((port->lock = SDL_CreateMutex()) == NULL)
+ return -1;
+
+ port->magic = PPORT_MAGIC;
+ port->ring = NULL;
+ DLIST_INIT(&port->messages);
+
+ return 0;
+}
+
+/*
+ * Returns TRUE if the supplied port is valid/usable, or FALSE otherwise.
+ * Useful to conditionally destroy a port, for instance.
+ */
+int
+thread_port_valid(thread_port_t *port)
+{
+
+ return (port != NULL && port->magic == PPORT_MAGIC);
+}
+
+/*
+ * Destroys the specified port, previously created using thread_port_init().
+ */
+void
+thread_port_destroy(thread_port_t *port)
+{
+
+ ASSERT(port != NULL && port->magic == PPORT_MAGIC);
+
+ port->magic = 0;
+ SDL_DestroyMutex(port->lock);
+}
+
+/*
+ * Attaches a port to a ring. Multiple ports may be attached to a ring. A
+ * message arriving on an empty port will cause the attached ring to be
+ * notified, if any, and as such to cause a thread waiting on the ring to
+ * be awakened.
+ */
+void
+thread_port_set_ring(thread_port_t *port, thread_ring_t *ring)
+{
+
+ ASSERT(port != NULL && port->magic == PPORT_MAGIC &&
+ (ring == NULL || ring->magic == PRING_MAGIC));
+
+ port->ring = ring;
+}
+
+/*
+ * Allows to initialize a message before it can be sent over a port. The
+ * message only needs to be initialized once in general, even if it will be
+ * used for bidirectional transmission for synchronous operation. If the
+ * reply port needs to be changed, however, this function should be used again
+ * to set the new reply port.
+ */
+void
+thread_msg_init(thread_msg_t *msg, thread_port_t *rport)
+{
+
+ ASSERT(msg != NULL && msg->magic != PMESG_MAGIC &&
+ (rport == NULL || rport->magic == PPORT_MAGIC));
+
+ msg->magic = PMESG_MAGIC;
+ msg->reply = rport;
+}
+
+/*
+ * Returns TRUE if supplied message is valid/usable or FALSE otherwise.
+ */
+int
+thread_msg_valid(thread_msg_t *msg)
+{
+
+ return (msg != NULL && msg->magic == PMESG_MAGIC);
+}
+
+/*
+ * Invalidates a message, so that it can no longer be sent over ports.
+ */
+void
+thread_msg_destroy(thread_msg_t *msg)
+{
+
+ ASSERT(msg != NULL && msg->magic == PMESG_MAGIC);
+
+ msg->magic = 0;
+}
+
+/*
+ * If any message exists in the queue of the specified port, unqueues it and
+ * returns it. Otherwise, NULL is returned. In normal operation, all messages
+ * queued to a port are processed before putting the thread back into sleep,
+ * mainly for efficiency, but also because it eases synchronization.
+ */
+thread_msg_t *
+thread_msg_get(thread_port_t *port)
+{
+ thread_msg_t *msg = NULL;
+
+ ASSERT(port != NULL && port->magic == PPORT_MAGIC);
+
+ if (SDL_LockMutex(port->lock) != 0)
+ goto err;
+
+ if ((msg = DLIST_TOP(&port->messages)) != NULL) {
+ ASSERT(msg->magic == PMESG_MAGIC);
+ DLIST_UNLINK(&port->messages, (node_t *)msg);
+ }
+
+ (void) SDL_UnlockMutex(port->lock);
+
+err:
+ return (thread_msg_t *)msg;
+}
+
+/*
+ * Queues the specified message to the specified port, returning 0 on success.
+ * Note that the message data is not copied or moved, but that a pointer
+ * system is used to queue the message. Thus, the message's shared memory
+ * region is leased temporarily to the other end. One has to be careful to
+ * not allocate this message space on the stack when asynchroneous operation
+ * is needed. In synchroneous operation mode, it is not a problem, since the
+ * sender does not have to modify the data until the other end replies back
+ * with the same message after modifying the message if necessary. In
+ * synchroneous mode, we simply delegate that message memory region to the
+ * other end until it notifies us with a reply that it is done working with
+ * it. Returns 0 on success, or -1 on error.
+ */
+int
+thread_msg_put(thread_port_t *port, thread_msg_t *msg)
+{
+
+ ASSERT(port != NULL && port->magic == PPORT_MAGIC &&
+ msg != NULL && msg->magic == PMESG_MAGIC);
+
+ if (SDL_LockMutex(port->lock) != 0)
+ return -1;
+
+ DLIST_APPEND(&port->messages, (node_t *)msg);
+ if (port->ring != NULL) {
+ if (DLIST_NODES(&port->messages) == 1) {
+ /*
+ * We know that there previously were no messages,
+ * and that the reading thread then waits for any
+ * message to be available. Signal it that there at
+ * least is one message ready. The other end should
+ * normally process all available messages before
+ * going back into waiting.
+ */
+ if (SDL_LockMutex(port->ring->mutex) == 0) {
+ port->ring->event = 1;
+ (void) SDL_CondSignal(port->ring->cond);
+ (void) SDL_UnlockMutex(port->ring->mutex);
+ }
+ }
+ }
+
+ (void) SDL_UnlockMutex(port->lock);
+
+ return 0;
+}
+
+/*
+ * Meant to be used in synchroneous message transfer mode. The initial sender
+ * sends a message to the other end, which then uses this function to notify
+ * back the initial sender that it is done, often with a success/failure
+ * result as part of the message. Returns 0 on success, or -1 on error.
+ */
+int
+thread_msg_reply(thread_msg_t *msg)
+{
+
+ ASSERT(msg != NULL && msg->magic == PMESG_MAGIC && msg->reply != NULL);
+
+ return thread_msg_put(msg->reply, msg);
+}
+
+/*
+ * Returns the number of pending messages tied to the port, if any, or -1
+ * on error.
+ */
+int
+thread_port_pending(thread_port_t *port)
+{
+ int pending = -1;
+
+ ASSERT(port != NULL && port->magic == PPORT_MAGIC);
+
+ if (SDL_LockMutex(port->lock) == 0) {
+ pending = (int)DLIST_NODES(&port->messages);
+ (void) SDL_UnlockMutex(port->lock);
+ }
+
+ return pending;
+}
+
+/*
+ * Initializes the asynchroneous messages pool and mutex. Must be called
+ * before thread_amsg_create() and thread_amsg_destroy() can be used.
+ * Returns 0 on success or -1 on error.
+ */
+int
+thread_amsg_pool_init(void)
+{
+
+ if (pool_init(&amsg_pool, "amsg_pool", malloc, free, amsg_create,
+ amsg_destroy, sizeof(thread_amsg_t), 64, 1, 0) == -1)
+ return -1;
+
+ if ((amsg_mutex = SDL_CreateMutex()) == NULL) {
+ pool_destroy(&amsg_pool);
+ return -1;
+ }
+
+ amsg_initialized = 1;
+
+ return 0;
+}
+
+static int
+amsg_create(pnode_t *p)
+{
+
+ thread_msg_init((thread_msg_t *)p, NULL);
+
+ return 0;
+}
+
+static void
+amsg_destroy(pnode_t *p)
+{
+
+ thread_msg_destroy((thread_msg_t *)p);
+}
+
+/*
+ * Creates an amsg and returns a pointer to it, or NULL on failure.
+ * This is useful to efficiently create/send asynchroneous messages to
+ * another thread which is expected to receive/destroy it asynchroneously
+ * without replying, in which case a pool of distinct messages must be used.
+ */
+thread_amsg_t *
+thread_amsg_create(void)
+{
+ thread_amsg_t *amsg = NULL;
+
+ ASSERT(amsg_initialized);
+
+ if (SDL_LockMutex(amsg_mutex) == 0) {
+ amsg = (thread_amsg_t *)pool_alloc(&amsg_pool, 0);
+ SDL_UnlockMutex(amsg_mutex);
+ amsg->callback = NULL;
+ }
+
+ return amsg;
+}
+
+void
+thread_amsg_setcallback(thread_amsg_t *msg,
+ void (*callback)(void *), void *callback_arg)
+{
+
+ ASSERT(msg != NULL);
+ msg->callback = callback;
+ msg->callback_arg = callback_arg;
+}
+
+void
+thread_amsg_callback(thread_amsg_t *msg)
+{
+
+ ASSERT(msg != NULL);
+
+ if (msg->callback != NULL)
+ msg->callback(msg->callback_arg);
+}
+
+/*
+ * Destroys an amsg previously created with thread_amsg_create().
+ */
+void
+thread_amsg_destroy(thread_amsg_t *amsg)
+{
+
+ ASSERT(amsg_initialized && amsg != NULL);
+
+ if (SDL_LockMutex(amsg_mutex) == 0) {
+ (void) pool_free((pnode_t *)amsg);
+ SDL_UnlockMutex(amsg_mutex);
+ } else
+ (void) fprintf(stderr,
+ "thread_amsg_destroy() - SDL_LockMutex() - %s",
+ SDL_GetError());
+}
--- /dev/null
+/* $Id: thread_msg.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Adapted from older code intended for use with POSIX threads,
+ * for use within SDL.
+ */
+
+
+
+#ifndef THREAD_MSG_H
+#define THREAD_MSG_H
+
+
+
+#include <stdint.h>
+
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#include <dlist.h>
+#include <pool.h>
+
+
+
+#define PRING_MAGIC 0x50524e47
+#define PPORT_MAGIC 0x50505254
+#define PMESG_MAGIC 0x504d5347
+
+#define AMSG_MAXSIZE 32768
+
+typedef struct {
+ uint32_t magic;
+ SDL_cond *cond;
+ SDL_mutex *mutex;
+ int event;
+} thread_ring_t;
+
+typedef struct {
+ uint32_t magic;
+ thread_ring_t *ring;
+ SDL_mutex *lock;
+ list_t messages;
+} thread_port_t;
+
+typedef struct {
+ pnode_t pnode;
+ uint32_t magic;
+ thread_port_t *reply;
+} thread_msg_t;
+
+typedef struct {
+ thread_msg_t msg;
+ void (*callback)(void *);
+ void *callback_arg;
+ size_t size;
+ uint32_t data[AMSG_MAXSIZE / 4];
+} thread_amsg_t;
+
+
+
+extern int thread_ring_init(thread_ring_t *);
+extern int thread_ring_valid(thread_ring_t *);
+extern void thread_ring_destroy(thread_ring_t *);
+extern int thread_ring_wait(thread_ring_t *, Uint32);
+extern int thread_ring_notify(thread_ring_t *);
+
+extern int thread_port_init(thread_port_t *);
+extern int thread_port_valid(thread_port_t *);
+extern void thread_port_destroy(thread_port_t *);
+extern void thread_port_set_ring(thread_port_t *, thread_ring_t *);
+extern void thread_msg_init(thread_msg_t *, thread_port_t *);
+extern int thread_msg_valid(thread_msg_t *);
+extern void thread_msg_destroy(thread_msg_t *);
+extern thread_msg_t *thread_msg_get(thread_port_t *);
+extern int thread_msg_put(thread_port_t *, thread_msg_t *);
+extern int thread_msg_reply(thread_msg_t *);
+extern int thread_port_pending(thread_port_t *);
+
+extern int thread_amsg_pool_init(void);
+extern thread_amsg_t *thread_amsg_create(void);
+extern void thread_amsg_setcallback(thread_amsg_t *,
+ void (*)(void *), void *);
+extern void thread_amsg_callback(thread_amsg_t *);
+extern void thread_amsg_destroy(thread_amsg_t *);
+
+
+
+#endif
--- /dev/null
+/* $Id: thread_net_recv.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * User events polling thread. Reports all events to the master thread
+ * via efficient inter-thread messages.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <main.h>
+#include <thread_net_recv.h>
+#include <recvq.h>
+#include <conf.h>
+
+
+
+static void ssend(thread_msg_t *);
+static int state_connect(void);
+static void state_recv(void);
+
+
+
+static thread_ring_t recv_ring;
+static thread_port_t recv_port;
+
+
+
+TCPsocket server_socket = NULL;
+recvq_t recvq;
+
+
+
+int
+thread_net_recv(void *data)
+{
+
+ /* Initialize reply port and ring */
+ if (thread_ring_init(&recv_ring) == -1) {
+ (void) fprintf(stderr,
+ "thread_net_recv() - thread_ring_init(recv_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if (thread_port_init(&recv_port) == -1) {
+ (void) fprintf(stderr,
+ "thread_net_recv() - thread_port_init(recv_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ thread_port_set_ring(&recv_port, &recv_ring);
+
+ if (state_connect() == 0)
+ state_recv();
+
+ /* Wait peacefully until our process exits */
+ for (;;)
+ (void) thread_ring_wait(&recv_ring, -1);
+
+ /* NOTREACHED */
+
+ return 0;
+}
+
+/*
+ * Sends a message synchroneously, that is, sends and waits for a reply.
+ */
+static void
+ssend(thread_msg_t *msg)
+{
+ thread_msg_t *rmsg;
+
+ if (thread_msg_put(&main_port, msg) == -1)
+ (void) fprintf(stderr,
+ "ssend() - thread_msg_put()\n");
+ while ((rmsg = thread_msg_get(&recv_port)) == NULL) {
+ if (thread_ring_wait(&recv_ring, -1) != 0)
+ (void) fprintf(stderr,
+ "ssend() - thread_ring_wait()\n");
+ }
+}
+
+/*
+ * Attempts to connect to server. On success, 0 is returned and a success
+ * reply packet is sent synchroneously to the main thread. On error, a
+ * failure packet is sent instead and we return -1.
+ */
+static int
+state_connect(void)
+{
+ struct msg_connect msg;
+ IPaddress addr;
+
+ thread_msg_init(&msg.msg, &recv_port);
+ msg.status = 0;
+ msg.error = NULL;
+
+ if (SDLNet_ResolveHost(&addr, SERVER_HOST, SERVER_PORT) != 0) {
+ msg.status = -1;
+ msg.error = "Could not resolve server hostname";
+ goto end;
+ }
+
+ if ((server_socket = SDLNet_TCP_Open(&addr)) == NULL) {
+ msg.status = -1;
+ msg.error = "Could not connect to server";
+ }
+
+ if (recvq_init(&recvq, server_socket, 16384) == -1) {
+ msg.status = -1;
+ msg.error = "Could not allocate memory for recvq buffer";
+ }
+
+end:
+ ssend(&msg.msg);
+
+ return 0;
+}
+
+/*
+ * We endlessly read packets from the server and queue them asynchroneously to
+ * the main thread's port, unless a read error occurs, in which case we queue
+ * an empty/error special packet and return.
+ */
+static void
+state_recv(void)
+{
+ thread_amsg_t *amsg = NULL;
+
+ for (;;) {
+ uint8_t *buf;
+ size_t size;
+ int res;
+
+ /*
+ * We must already have an allocated message since we'll read
+ * data directly into its buffer. If this operation fails,
+ * it's fatal since the receiving main thread will never be
+ * notified.
+ */
+ if ((amsg = thread_amsg_create()) == NULL) {
+ (void) fprintf(stderr,
+ "state_recv() - thread_amsg_create()\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Call recvq_read() until we either have 1, in which case
+ * we'll send an async message, or -1 for error.
+ */
+ while ((res = recvq_read(&recvq)) == 0) ;
+
+ if (res == -1) {
+ (void) fprintf(stderr,
+ "Connection with server lost\n");
+ break;
+ }
+
+ /*
+ * res == 1. We therefore can fill our message and dispatch
+ * it asynchroneously.
+ */
+ recvq_content(&recvq, &buf, &size);
+ if (size > AMSG_MAXSIZE) {
+ (void) fprintf(stderr,
+ "state_recv() - increase AMSG_MAXSIZE!\n");
+ exit(EXIT_FAILURE);
+ }
+ (void) memcpy(amsg->data, buf, size);
+ amsg->size = size;
+ recvq_content_reset(&recvq);
+
+ /* Asynchroneously send packet to the main thread */
+ (void) thread_msg_put(&main_port, &amsg->msg);
+ }
+
+ /* Send the end of stream message to the main thread */
+ amsg->size = -1;
+ (void) thread_msg_put(&main_port, &amsg->msg);
+}
--- /dev/null
+/* $Id: thread_net_recv.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * User events polling thread. Reports all events to the master thread
+ * via efficient inter-thread messages.
+ */
+
+
+
+#ifndef THREAD_NET_RECV_H
+#define THREAD_NET_RECV_H
+
+
+
+#include <SDL.h>
+#include <SDL_net.h>
+
+#include <thread_msg.h>
+
+
+
+/*
+ * The initial message we're waiting for from the read thread. The read
+ * thread will attempt to connect to the server, initiate authentication and
+ * then reply using this message with the connection status. Afterwards, we
+ * only expect msg_read packets from that thread.
+ * <status> will be 0 on success or -1 on failure, in which case the error
+ * message string will be found into <error>.
+ */
+struct msg_connect {
+ thread_msg_t msg;
+ int status;
+ const char *error;
+};
+
+/*
+ * We receive such a message when a packet was successfully read from the
+ * server. We reply in a synchroneous manner, however the reply delay should
+ * be very short compared to the possible delay of the read thread during a
+ * blocking read (SDL_net does not support non-blocking I/O operations).
+ * If -1 is received for the <data> size, with a NULL <data> pointer, it means
+ * that the connection status was lost or that a timeout occurred.
+ */
+struct msg_recv {
+ thread_msg_t msg;
+ void *data;
+ size_t size;
+};
+
+
+
+int thread_net_recv(void *);
+
+
+
+extern TCPsocket server_socket;
+
+
+
+#endif
--- /dev/null
+/* $Id: thread_net_send.c,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Server socket writing thread. We post all asynchroneous messages
+ * (thread_amsg_t) we receive from the main thread to the server socket.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <conf.h>
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+#include <thread_net_send.h>
+#include <thread_net_recv.h>
+#ifdef USE_ENCRYPTION
+#include <enc.h>
+#endif
+
+
+
+#define DBUFFER_SIZE 4096
+
+
+
+static thread_ring_t send_ring;
+#ifdef USE_COMPRESSION
+static char dbuffer[DBUFFER_SIZE];
+#endif
+
+
+
+thread_port_t send_port;
+
+
+
+int
+thread_net_send(void *data)
+{
+ thread_amsg_t *amsg;
+#ifdef USE_COMPRESSION
+ z_stream zout;
+
+ /* Initialize outgoing data compressor */
+ zout.next_in = zout.next_out = NULL;
+ zout.avail_in = zout.avail_out = 0;
+ zout.zalloc = NULL;
+ zout.zfree = NULL;
+ zout.opaque = NULL;
+ if (deflateInit(&zout, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ (void) fprintf(stderr, "thread_net_recv() - deflateInit()\n");
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ /* Initialize reply port and ring */
+ if (thread_ring_init(&send_ring) == -1) {
+ (void) fprintf(stderr,
+ "thread_net_recv() - thread_ring_init(send_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ if (thread_port_init(&send_port) == -1) {
+ (void) fprintf(stderr,
+ "thread_net_send() - thread_port_init(send_port) - %s\n",
+ SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+ thread_port_set_ring(&send_port, &send_ring);
+
+ /*
+ * Simply process all incomming messages, sending their data to
+ * the server port, and discarding the messages. When the port is
+ * empty, wait polling until messages become available.
+ */
+ for (;;) {
+ while ((amsg = (thread_amsg_t *)thread_msg_get(&send_port))
+ != NULL) {
+ uint8_t *buf;
+ size_t s;
+
+#ifdef USE_COMPRESSION
+ /* Compress outgoing data */
+ zout.next_in = (void *)amsg->data;
+ zout.avail_in = amsg->size;
+ zout.next_out = (Bytef *)dbuffer;
+ zout.avail_out = DBUFFER_SIZE;
+ if (deflate(&zout, Z_SYNC_FLUSH) != Z_OK) {
+ (void) fprintf(stderr,
+ "thread_net_send() - deflate()\n");
+ exit(EXIT_FAILURE);
+ }
+ buf = (uint8_t *)dbuffer;
+ s = DBUFFER_SIZE - zout.avail_out;
+#else
+ buf = (uint8_t *)amsg->data;
+ s = amsg->size;
+#endif /* USE_COMPRESSION */
+ if (s > 0) {
+#ifdef USE_ENCRYPTION
+ /* Encrypt outgoing data */
+ if (mmencrypt)
+ mmenc_encrypt(&enc_out, buf, s);
+#endif
+ if (SDLNet_TCP_Send(server_socket, buf, s)
+ != s)
+ (void) fprintf(stderr,
+ "Error writing to server socket\n");
+ }
+ thread_amsg_callback(amsg);
+ thread_amsg_destroy(amsg);
+ }
+ (void) thread_ring_wait(&send_ring, -1);
+ }
+
+ /* NOTREACHED */
+
+ return 0;
+}
--- /dev/null
+/* $Id: thread_net_send.h,v 1.1 2006/12/31 08:32:39 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Server socket writing thread. We post all asynchroneous messages
+ * (thread_amsg_t) we receive from the main thread to the server socket.
+ */
+
+
+
+#ifndef THREAD_NET_SEND_H
+#define THREAD_NET_SEND_H
+
+
+
+#include <SDL.h>
+#include <SDL_net.h>
+
+#include <thread_msg.h>
+
+
+
+int thread_net_send(void *);
+
+
+
+extern thread_port_t send_port;
+
+
+
+#endif
--- /dev/null
+/* $Id: encrypt_mmenc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mmenc.h>
+
+
+
+#define RBUF_SIZE 4096
+
+
+
+int main(int, char **);
+
+static mmenc_t *key_load(const char *);
+static void key_free(mmenc_t *);
+
+
+
+int
+main(int argc, char **argv)
+{
+ char rbuf[RBUF_SIZE];
+ size_t size;
+ mmenc_t *ctx;
+
+ if (argc != 2) {
+ (void) fprintf(stderr, "Usage: mmenc <4096-bit-keyfile>\n");
+ exit(EXIT_FAILURE);
+ }
+ if ((ctx = key_load(argv[1])) == NULL) {
+ perror("key_load()");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setvbuf(stdin, NULL, _IONBF, 0) == EOF)
+ perror("setvbuf(stdin)");
+ if (setvbuf(stdout, NULL, _IONBF, 0) == EOF)
+ perror("setvbuf(stdout)");
+
+ while (!feof(stdin) && !ferror(stdin)) {
+ if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1)
+ continue;
+ mmenc_encrypt(ctx, rbuf, size);
+ if (fwrite(rbuf, 1, size, stdout) != size) {
+ perror("fwrite()");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ key_free(ctx);
+
+ exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static mmenc_t *
+key_load(const char *file)
+{
+ mmenc_t *ctx = NULL;
+ uint8_t *key = NULL;
+ FILE *fh = NULL;
+
+ if ((ctx = malloc(sizeof(mmenc_t))) == NULL ||
+ (key = malloc(512)) == NULL) {
+ perror("malloc()");
+ goto err;
+ }
+
+ if ((fh = fopen(file, "r")) == NULL) {
+ perror("fopen()");
+ goto err;
+ }
+ (void) setvbuf(fh, NULL, _IONBF, 0);
+ if ((fread(key, 1, 512, fh)) != 512) {
+ perror("fread()");
+ goto err;
+ }
+
+ (void) fclose(fh);
+ mmenc_init(ctx, key, 512, 4096);
+ (void) memset(key, '\0', 512);
+ free(key);
+
+ return ctx;
+
+err:
+ if (fh)
+ (void) fclose(fh);
+ if (key) {
+ (void) memset(key, '\0', 512);
+ free(key);
+ }
+ if (ctx) {
+ mmenc_destroy(ctx);
+ free(ctx);
+ }
+
+ return NULL;
+}
+
+static void
+key_free(mmenc_t *key)
+{
+
+ mmenc_destroy(key);
+ free(key);
+}
--- /dev/null
+/* $Id: encrypt_rc4.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rc4.h>
+
+
+
+#define RBUF_SIZE 4096
+
+
+
+int main(int, char **);
+
+static rc4_t *key_load(const char *);
+static void key_free(rc4_t *);
+
+
+
+int
+main(int argc, char **argv)
+{
+ char rbuf[RBUF_SIZE];
+ size_t size;
+ rc4_t *ctx;
+
+ if (argc != 2) {
+ (void) fprintf(stderr, "Usage: rc4 <4096-bit-keyfile>\n");
+ exit(EXIT_FAILURE);
+ }
+ if ((ctx = key_load(argv[1])) == NULL) {
+ perror("key_load()");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setvbuf(stdin, NULL, _IONBF, 0) == EOF)
+ perror("setvbuf(stdin)");
+ if (setvbuf(stdout, NULL, _IONBF, 0) == EOF)
+ perror("setvbuf(stdout)");
+
+ while (!feof(stdin) && !ferror(stdin)) {
+ if ((size = fread(rbuf, 1, RBUF_SIZE, stdin)) < 1)
+ continue;
+ rc4_encrypt(ctx, rbuf, size);
+ if (fwrite(rbuf, 1, size, stdout) != size) {
+ perror("fwrite()");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ key_free(ctx);
+
+ exit(feof(stdin) ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static rc4_t *
+key_load(const char *file)
+{
+ rc4_t *ctx = NULL;
+ uint8_t *key = NULL;
+ FILE *fh = NULL;
+
+ if ((ctx = malloc(sizeof(rc4_t))) == NULL ||
+ (key = malloc(512)) == NULL) {
+ perror("malloc()");
+ goto err;
+ }
+
+ if ((fh = fopen(file, "r")) == NULL) {
+ perror("fopen()");
+ goto err;
+ }
+ (void) setvbuf(fh, NULL, _IONBF, 0);
+ if ((fread(key, 1, 512, fh)) != 512) {
+ perror("fread()");
+ goto err;
+ }
+
+ (void) fclose(fh);
+ rc4_init(ctx, key, 512, 4096);
+ (void) memset(key, '\0', 512);
+ free(key);
+
+ return ctx;
+
+err:
+ if (fh)
+ (void) fclose(fh);
+ if (key) {
+ (void) memset(key, '\0', 512);
+ free(key);
+ }
+ if (ctx) {
+ rc4_destroy(ctx);
+ free(ctx);
+ }
+
+ return NULL;
+}
+
+static void
+key_free(rc4_t *key)
+{
+
+ rc4_destroy(key);
+ free(key);
+}
--- /dev/null
+/* $Id: hmac.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * ANSIfied and cleaned up by Matthew Mondor, 2006
+ */
+
+/*
+ * Copyright (c) 2004, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Implement HMAC as described in RFC 2104
+ *
+ * You need to define the following before including this file.
+ *
+ * HMAC_FUNC the name of the function (hmac_sha1 or hmac_md5 etc)
+ * HASH_LENGTH the size of the digest (20 for SHA1, 16 for MD5)
+ * HASH_BLOCKSZ size of internal block size (64 for SHA1 and MD5)
+ * HASH_CTX the name of the HASH CTX
+ * HASH_Init
+ * HASH_Update
+ * Hash_Final
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hmac.h>
+
+
+
+/* Don't change these */
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+
+
+
+/*
+ * The logic here is lifted straight from RFC 2104 except that
+ * rather than filling the pads with 0, copying in the key and then
+ * XOR with the pad byte, we just fill with the pad byte and
+ * XOR with the key.
+ */
+void
+HMAC_FUNC(const uint8_t *text, size_t text_len,
+ const uint8_t *key, size_t key_len, uint8_t *digest)
+{
+ HASH_CTX context;
+ /* Inner padding key XOR'd with ipad */
+ uint8_t k_ipad[HASH_BLOCKSZ + 1];
+ /* Outer padding key XOR'd with opad */
+ uint8_t k_opad[HASH_BLOCKSZ + 1];
+ /* HASH(key) if needed */
+ uint8_t tk[HASH_LENGTH];
+ int i;
+
+ /*
+ * If key is longer than HASH_BLOCKSZ bytes
+ * reset it to key=HASH(key)
+ */
+ if (key_len > HASH_BLOCKSZ) {
+ HASH_CTX tctx;
+
+ HASH_Init(&tctx);
+ HASH_Update(&tctx, key, key_len);
+ HASH_Final(tk, &tctx);
+
+ key = tk;
+ key_len = HASH_LENGTH;
+ }
+
+ /*
+ * The HMAC_ transform looks like:
+ *
+ * HASH(K XOR opad, HASH(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte HMAC_IPAD repeated HASH_BLOCKSZ times
+ * opad is the byte HMAC_OPAD repeated HASH_BLOCKSZ times
+ * and text is the data being protected
+ */
+
+ /*
+ * Fill the pads and XOR in the key
+ */
+ (void) memset(k_ipad, HMAC_IPAD, sizeof(k_ipad));
+ (void) memset(k_opad, HMAC_OPAD, sizeof(k_opad));
+ for (i = 0; i < key_len; i++) {
+ k_ipad[i] ^= key[i];
+ k_opad[i] ^= key[i];
+ }
+
+ /*
+ * Perform inner HASH.
+ * Start with inner pad,
+ * then the text.
+ */
+ HASH_Init(&context);
+ HASH_Update(&context, k_ipad, HASH_BLOCKSZ);
+ HASH_Update(&context, text, text_len);
+ HASH_Final(digest, &context);
+
+ /*
+ * Perform outer HASH.
+ * Start with the outer pad,
+ * then the result of the inner hash.
+ */
+ HASH_Init(&context);
+ HASH_Update(&context, k_opad, HASH_BLOCKSZ);
+ HASH_Update(&context, digest, HASH_LENGTH);
+ HASH_Final(digest, &context);
+}
--- /dev/null
+/* $Id: hmac.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+#ifndef _HMAC_H_
+#define _HMAC_H_
+
+
+
+void hmac_sha1(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *);
+void hmac_rmd160(const uint8_t *, size_t, const uint8_t *, size_t, uint8_t *);
+
+
+
+#endif
--- /dev/null
+/* $Id: hmac_rmd160.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: hmac_sha1.c,v 1.2 2005/02/09 21:35:46 kleink Exp $ */
+
+/*
+ * hmac_rmd160 - using HMAC from RFC 2104
+ */
+
+#ifndef _HMAC_RMD160_
+#define _HMAC_RMD160_
+
+
+
+#include <rmd160.h>
+
+
+
+#define HMAC_FUNC hmac_rmd160
+#define HASH_BLOCKSZ 64
+#define HASH_LENGTH 20
+#define HASH_CTX rmd160_ctx_t
+#define HASH_Init rmd160_init
+#define HASH_Update rmd160_update
+#define HASH_Final rmd160_final
+
+
+
+#include <hmac.c>
+
+
+
+#endif
--- /dev/null
+/* $Id: hmac_sha1.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: hmac_sha1.c,v 1.2 2005/02/09 21:35:46 kleink Exp $ */
+
+/*
+ * hmac_sha1 - using HMAC from RFC 2104
+ */
+
+#ifndef _HMAC_SHA1_
+#define _HMAC_SHA1_
+
+
+
+#include <sha1.h>
+
+
+
+#define HMAC_FUNC hmac_sha1
+#define HASH_BLOCKSZ 64
+#define HASH_LENGTH 20
+#define HASH_CTX sha1_ctx_t
+#define HASH_Init sha1_init
+#define HASH_Update sha1_update
+#define HASH_Final sha1_final
+
+
+
+#include <hmac.c>
+
+
+
+#endif
--- /dev/null
+/* $Id: mmenc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/* This code illustrates a sample implementation
+ * of the Arcfour algorithm
+ * Copyright (c) April 29, 1997 Kalle Kaukonen.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that this copyright
+ * notice and disclaimer are retained.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE
+ * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (C) 2000-2006, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This consists of an rc4 variation which is to be used with 512-bit keys,
+ * as opposed to the original implementation made to be used with 128-bit
+ * ones. The matrix has 4096 entries (64x64) rather than 256 (16x16).
+ * Moreover, a z axis was added, transforming on x like x does on y, but
+ * with an increment of 3 steps rather than 1. It is recommnded to tell
+ * mmenc_init() to discard/skip 4999 or 16369 bytes of the PRNG keystream.
+ * Some aspects of the key scheduling alrorithm were also modified.
+ *
+ *
+ * KNOWN ARCFOUR RELATED SECURITY CONSIDERATIONS
+ * =============================================
+ *
+ * The keystream generated by RC4 is slightly biased in favour of certain
+ * sequences of bytes. The best attack based on this bias is due to Fluhrer
+ * and McGrew, which will distinguish the keystream from a random stream given
+ * a gigabyte of output. To counter this, the session key should be
+ * regenerated after either a certain delay or after enough number of bytes
+ * have been processed through it.
+ *
+ * RC4 does not take a separate nonce alongside the key. As with any cipher,
+ * but particularly with Vernam ciphers, such a nonce is a requirement for
+ * security, so that encrypting the same message twice produces a different
+ * ciphertext each time. A secure solution to this that works for any secure
+ * cipher is to generate each RC4 key by hashing a long-term key with a unique
+ * nonce using a construction such as HMAC. However, many applications that
+ * use RC4 simply concatenate key and nonce; RC4's weak key schedule then
+ * gives rise to a variety of serious problems.
+ *
+ * In 2001 a new and surprising discovery was made by Fluhrer, Mantin and
+ * Shamir: over all possible RC4 keys, the statistics for the first few bytes
+ * of output keystream are strongly non-random, leaking information about the
+ * key. If the long-term key and nonce are simply concatenated to generate the
+ * RC4 key, this long-term key can be discovered by analysing large number of
+ * messages encrypted with this key. This and related effects were then used
+ * to break the WEP ("wired equivalent privacy") encryption used with 802.11
+ * wireless networks. This caused a scramble for a standards-based replacement
+ * for WEP in the 802.11 market, and led to the IEEE 802.11i effort and WPA.
+ * Cryptosystems can defend against this attack by discarding the initial
+ * portion of the keystream (say the first 1024 bytes) before using it.
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+
+#include <mmenc.h>
+
+
+
+/*
+ * XXX Hmm would it be better to seed using 32-bit at a time form the key
+ * and avoid the * 16 part?
+ */
+void
+mmenc_init(mmenc_t *ctx, uint8_t key[512], size_t keylen)
+{
+ register unsigned int i, t, u, ki, si, *state;
+
+ if (keylen == 0)
+ key[keylen++] = 37;
+ for (ki = 0; keylen < 512; keylen++) {
+ key[keylen] = ((key[ki] + 1) * keylen) & 0xff;
+ if (++ki >= keylen)
+ ki = 0;
+ }
+
+ state = ctx->state;
+ ctx->x = 23;
+ ctx->y = 127;
+ ctx->z = 251;
+ for (i = 0; i < 4096; i++)
+ state[i] = i;
+ ki = si = 0;
+ for (i = 0; i < 4096; i++) {
+ t = state[i];
+ si = (si + (((int)key[ki]) * 16) + t) & 0xfff;
+ u = state[si];
+ state[si] = t;
+ state[i] = u;
+ if (++ki >= keylen)
+ ki = 0;
+ }
+
+ {
+ register unsigned int x, y, z, sx, sz;
+
+ x = ctx->x;
+ y = ctx->y;
+ z = ctx->z;
+ for (i = 0; i < 4999; i++) {
+
+ x = (x + 1) & 0xfff;
+ sx = state[x];
+ y = (sx + y) & 0xfff;
+ state[x] = state[y];
+ state[y] = sx;
+
+ z = (z + 3) & 0xfff;
+ sz = state[z];
+ x = (sz + x) & 0xfff;
+ state[z] = state[x];
+ state[x] = sz;
+ }
+ ctx->x = x;
+ ctx->y = y;
+ ctx->z = z;
+ }
+}
+
+void
+mmenc_destroy(mmenc_t *ctx)
+{
+
+ (void) memset(ctx, '\0', sizeof(mmenc_t));
+}
+
+void
+mmenc_random(mmenc_t *ctx, uint8_t *buf, size_t len)
+{
+ register unsigned int x, y, z, sx, sy, sz, *state;
+ register uint8_t *endbuf;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ z = ctx->z;
+ for (endbuf = buf + len; buf < endbuf; buf++) {
+ x = (x + 1) & 0xfff;
+ sx = state[x];
+ y = (sx + y) & 0xfff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+
+ z = (z + 3) & 0xfff;
+ sz = state[z];
+ x = (sz + x) & 0xfff;
+ state[z] = sx = state[x];
+ state[x] = sz;
+
+ *buf = state[(sx + sy + sz) & 0xfff] & 0xff;
+ }
+ ctx->x = x;
+ ctx->y = y;
+ ctx->z = z;
+}
+
+void
+mmenc_seed(mmenc_t *ctx, const uint8_t *buf, size_t len)
+{
+ register unsigned int x, y, z, sx, sy, sz, *state;
+ register const uint8_t *endbuf;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ z = ctx->z;
+ for (endbuf = buf + len; buf < endbuf; buf++) {
+
+ x = (x + 1) & 0xfff;
+ sx = state[x];
+ y = (sx + y) & 0xfff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+
+ z = (z + 3) & 0xfff;
+ sz = state[z];
+ x = (sz + x) & 0xfff;
+ state[z] = sx = state[x];
+ state[x] = sz;
+
+ /*
+ * XXX Again using the * 16 oddity. We probably could also
+ * seed using 32-bit values.
+ */
+ state[(sx + sy + sz) & 0xfff] ^= ((*buf + 1) * 16) & 0xfff;
+ }
+ ctx->x = x;
+ ctx->y = y;
+ ctx->z = z;
+}
+
+void
+mmenc_encrypt(mmenc_t *ctx, uint8_t *buf, size_t len)
+{
+ register unsigned int x, y, z, sx, sy, sz, *state;
+ register uint8_t *endbuf;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ z = ctx->z;
+ for (endbuf = buf + len; buf < endbuf; buf++) {
+
+ x = (x + 1) & 0xfff;
+ sx = state[x];
+ y = (sx + y) & 0xfff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+
+ z = (z + 3) & 0xfff;
+ sz = state[z];
+ x = (sz + x) & 0xfff;
+ state[z] = sx = state[x];
+ state[x] = sz;
+
+ *buf ^= state[(sx + sy + sz) & 0xfff] & 0xff;
+ }
+ ctx->x = x;
+ ctx->y = y;
+ ctx->z = z;
+}
+
+void
+mmenc_encrypt2(mmenc_t *ctx, uint8_t *dst, const uint8_t *src, size_t len)
+{
+ register unsigned int x, y, z, sx, sy, sz, *state;
+ register const uint8_t *endsrc;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ z = ctx->z;
+ for (endsrc = src + len; src < endsrc; src++, dst++) {
+
+ x = (x + 1) & 0xfff;
+ sx = state[x];
+ y = (sx + y) & 0xfff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+
+ z = (z + 3) & 0xfff;
+ sz = state[z];
+ x = (sz + x) & 0xfff;
+ state[z] = sx = state[x];
+ state[x] = sz;
+
+ *dst = *src ^ (state[(sx + sy + sz) & 0xfff] & 0xff);
+ }
+ ctx->x = x;
+ ctx->y = y;
+ ctx->z = z;
+}
--- /dev/null
+/* $Id: mmenc.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2000-2004, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+
+#ifndef _MMENC_H_
+#define _MMENC_H_
+
+
+
+#include <sys/types.h>
+#include <stdint.h>
+
+
+
+#define mmenc_decrypt mmenc_encrypt
+#define mmenc_decrypt2 mmenc_encrypt2
+
+
+
+typedef struct mmenc {
+ int x, y, z;
+ unsigned int state[4096];
+} mmenc_t;
+
+
+
+void mmenc_init(mmenc_t *, uint8_t key[512], size_t);
+void mmenc_destroy(mmenc_t *);
+void mmenc_random(mmenc_t *, uint8_t *, size_t);
+void mmenc_seed(mmenc_t *, const uint8_t *, size_t);
+void mmenc_encrypt(mmenc_t *, uint8_t *, size_t);
+void mmenc_encrypt2(mmenc_t *, uint8_t *, const uint8_t *, size_t);
+
+
+
+#endif
--- /dev/null
+/* $Id: packets_common.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * All packets must begin with an int16_t packet_type. All fields of a packet
+ * must either be [u]int8_t, [u]int16_t, [u]int32_t or [u]int64_t. Moreover,
+ * all fields will be passed in network/big endian byte order when sent over
+ * the network. We could have used a union, but this would have resulted in
+ * larger, fixed sized packets wasting bandwidth. Since a server to client
+ * update will generally involve sending more than one such packet in a row,
+ * it is important to minimize their size.
+ * Note: If using 32-bit fields within a packet, make sure that they are
+ * 32-bit aligned.
+ */
+
+
+
+#ifndef _PACKETS_COMMON_H_
+#define _PACKETS_COMMON_H_
+
+
+
+#include <stdint.h>
+
+
+
+/*
+ * Server to client packets
+ */
+
+enum spacket_types {
+ SPACKET_AUTH = 0,
+ SPACKET_PING,
+ SPACKET_PONG,
+ SPACKET_EOF,
+ SPACKET_SHIP,
+ SPACKET_SHIPINFO,
+ SPACKET_TORP,
+ SPACKET_COLLISION,
+ SPACKET_MAX
+};
+
+enum svpacket_types {
+ SVPACKET_MESSAGE = SPACKET_MAX,
+ SVPACKET_MAX
+};
+
+#define SHIPF_SHIELD (1 << 0)
+#define SHIPF_CLOAK (1 << 1)
+
+/* Used to request client authentication and respond success/failure */
+struct spacket_auth {
+ int8_t packet_type;
+ int8_t protocol_version;
+ /* 32-bit time_t + high 32-bit counter + 24 random bytes */
+ uint8_t noncerand1[32], noncerand2[32];
+} __attribute__((__packed__));
+
+/* And for server to test idle connections */
+struct spacket_ping {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* And clients to test latency */
+struct spacket_pong {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* To notify end of frame data */
+struct spacket_eof {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* Ship position/direction update to client */
+struct spacket_ship {
+ int8_t packet_type;
+ int8_t flags;
+ int16_t id;
+ int16_t x, y;
+ uint8_t angle;
+} __attribute__((__packed__));
+
+/*
+ * Information on own's ship, possibly sent once per second.
+ * Possibly that we could send such packet at start to describe your ship's
+ * maximum capabilities, too.
+ */
+struct spacket_shipinfo {
+ int8_t packet_type;
+ int8_t thrust;
+ int16_t fuel;
+ int16_t shield;
+ int16_t hull;
+} __attribute__((__packed__));
+
+/* Torp to client */
+struct spacket_torp {
+ int8_t packet_type;
+ int8_t radius; /* XXX Add flags/team */
+ int16_t x, y; /* XXX Add angle? */
+} __attribute__((__packed__));
+
+/* Collision/detonation update to client */
+struct spacket_collision {
+ int8_t packet_type;
+ int8_t collision_type;
+ int16_t object1_id, object2_id;
+} __attribute__((__packed__));
+
+struct svpacket_message {
+ int8_t packet_type;
+ int8_t packet_length;
+ int8_t destination;
+ char string[0];
+} __attribute__((__packed__));
+
+
+
+/*
+ * Client to server packets
+ */
+
+enum cpacket_tyoes {
+ CPACKET_AUTH = 0,
+ CPACKET_PING,
+ CPACKET_PONG,
+ CPACKET_DIRECTION,
+ CPACKET_THRUST_ACC,
+ CPACKET_THRUST_DEC,
+ CPACKET_SHIELD,
+ CPACKET_CLOAK,
+ CPACKET_TORP,
+ CPACKET_SLOW,
+ CPACKET_FAST,
+ CPACKET_QUIT,
+ CPACKET_MAX
+};
+
+enum cvpacket_types {
+ CVPACKET_MESSAGE = CPACKET_MAX,
+ CVPACKET_MAX
+};
+
+/* Used to respond to server authentication request */
+struct cpacket_auth {
+ int8_t packet_type;
+ int8_t protocol_version;
+ int8_t login[16];
+ uint8_t hmac1[20], hmac2[20];
+} __attribute__((__packed__));
+
+/* To ping server for latency mesurement */
+struct cpacket_ping {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* To respond to server ping requests */
+struct cpacket_pong {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* Angle/direction change request */
+struct cpacket_direction {
+ int8_t packet_type;
+ uint8_t angle;
+} __attribute__((__packed__));
+
+/* Speed change requests */
+struct cpacket_thrust_acc {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+struct cpacket_thrust_dec {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+struct cpacket_shield {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+struct cpacket_cloak {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* Torpedo fire request */
+struct cpacket_torp {
+ int8_t packet_type;
+ uint8_t angle;
+} __attribute__((__packed__));
+
+/* Tells server to skip one more frame */
+struct cpacket_slow {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* Or to skip less */
+struct cpacket_fast {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+/* Game quit request */
+struct cpacket_quit {
+ int8_t packet_type;
+} __attribute__((__packed__));
+
+struct cvpacket_message {
+ int8_t packet_type;
+ int8_t packet_length;
+ int8_t destination;
+ char string[0];
+} __attribute__((__packed__));
+
+
+
+#endif
--- /dev/null
+/* $Id: rc4.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/* This code illustrates a sample implementation
+ * of the Arcfour algorithm
+ * Copyright (c) April 29, 1997 Kalle Kaukonen.
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that this copyright
+ * notice and disclaimer are retained.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KALLE KAUKONEN AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KALLE
+ * KAUKONEN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (C) 2000-2004, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Obviously, it is very important that every state use a totally new unique
+ * key. If two keys are ever the same, holes become available for deciphering.
+ * - Matt
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rc4.h>
+
+
+
+void
+rc4_init(rc4_t *ctx, const uint8_t *key, size_t len, size_t skip)
+{
+ register unsigned int i, t, u, ki, si, *state;
+
+ state = ctx->state;
+ ctx->x = 0;
+ ctx->y = 0;
+ for (i = 0; i < 256; i++)
+ state[i] = i;
+ ki = si = 0;
+ for (i = 0; i < 256; i++) {
+ t = state[i];
+ si = (si + key[ki] + t) & 0xff;
+ u = state[si];
+ state[si] = t;
+ state[i] = u;
+ if (++ki >= len)
+ ki = 0;
+ }
+
+ /*
+ * If skip is non-zero, skip the number of bytes from the keystream.
+ * It is recommended to at least skip 1024 bytes to avoid a known RC4
+ * vulnerability.
+ */
+ if (skip != 0) {
+ register unsigned int x, y, sx;
+
+ x = ctx->x;
+ y = ctx->y;
+ for (i = 0; i < skip; i++) {
+ x = (x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + y) & 0xff;
+ state[x] = state[y];
+ state[y] = sx;
+ }
+ ctx->x = x;
+ ctx->y = y;
+ }
+}
+
+
+void
+rc4_destroy(rc4_t *ctx)
+{
+
+ (void) memset(ctx, '\0', sizeof(rc4_t));
+}
+
+
+void
+rc4_encrypt(rc4_t *ctx, uint8_t *buf, size_t len)
+{
+ register unsigned int x, y, sx, sy, *state;
+ register uint8_t *endbuf;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ for (endbuf = buf + len; buf < endbuf; buf++) {
+ x = (x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + y) & 0xff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+ *buf ^= state[(sx + sy) & 0xff];
+ }
+ ctx->x = x;
+ ctx->y = y;
+}
+
+
+void
+rc4_encrypt2(rc4_t *ctx, uint8_t *dst, const uint8_t *src, size_t len)
+{
+ register unsigned int x, y, sx, sy, *state;
+ register const uint8_t *endsrc;
+
+ state = ctx->state;
+ x = ctx->x;
+ y = ctx->y;
+ for (endsrc = src + len; src < endsrc; src++, dst++) {
+ x = (x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + y) & 0xff;
+ state[x] = sy = state[y];
+ state[y] = sx;
+ *dst = *src ^ state[(sx + sy) & 0xff];
+ }
+ ctx->x = x;
+ ctx->y = y;
+}
--- /dev/null
+/* $Id: rc4.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (C) 2000-2004, Matthew Mondor
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Matthew Mondor.
+ * 4. The name of Matthew Mondor may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ * any GNU Public License derivate.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+
+
+#ifndef MM_RC4_H
+#define MM_RC4_H
+
+
+
+#include <stdint.h>
+
+
+
+#define rc4_decrypt rc4_encrypt
+#define rc4_decrypt2 rc4_encrypt2
+
+
+
+typedef struct rc4 {
+ int x, y, state[256];
+} rc4_t;
+
+
+
+void rc4_init(rc4_t *, const uint8_t *, size_t, size_t);
+void rc4_destroy(rc4_t *);
+void rc4_encrypt(rc4_t *, uint8_t *, size_t);
+void rc4_encrypt2(rc4_t *, uint8_t *, const uint8_t *, size_t);
+
+
+
+#endif
--- /dev/null
+/* $Id: rmd160.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: rmd160.c,v 1.8 2003/10/27 00:12:42 lukem Exp $ */
+
+/*
+ * ANSIfied and cleaned up by Matthew Mondor, 2006
+ */
+
+/********************************************************************\
+ *
+ * FILE: rmd160.c
+ *
+ * CONTENTS: A sample C-implementation of the RIPEMD-160
+ * hash-function.
+ * TARGET: any computer with an ANSI C compiler
+ *
+ * AUTHOR: Antoon Bosselaers, ESAT-COSIC
+ * (Arranged for libc by Todd C. Miller)
+ * DATE: 1 March 1996
+ * VERSION: 1.0
+ *
+ * Copyright (c) Katholieke Universiteit Leuven
+ * 1996, All Rights Reserved
+ *
+\********************************************************************/
+
+/* header files */
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __NetBSD__
+#include <machine/endian.h>
+#endif
+
+#include <rmd160.h>
+
+
+
+#ifndef BYTE_ORDER
+#error "BYTEORDER undefined!"
+#endif
+
+
+
+/* macro definitions */
+
+/* collect four bytes into one word: */
+typedef uint8_t * uint8ptr_t;
+#define BYTES_TO_DWORD(uint8ptr_t) \
+ (((uint32_t) *((uint8_t *) + 3) << 24) | \
+ ((uint32_t) *((uint8_t *) + 2) << 16) | \
+ ((uint32_t) *((uint8_t *) + 1) << 8) | \
+ ((uint32_t) *(uint8_t *)))
+
+/* ROL(x, n) cyclically rotates x over n bits to the left */
+/* x must be of an unsigned 32 bits type and 0 <= n < 32. */
+#define ROL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/* the three basic functions F(), G() and H() */
+#define F(x, y, z) ((x) ^ (y) ^ (z))
+#define G(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define H(x, y, z) (((x) | ~(y)) ^ (z))
+#define I(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define J(x, y, z) ((x) ^ ((y) | ~(z)))
+
+/* the eight basic operations FF() through III() */
+#define FF(a, b, c, d, e, x, s) { \
+ (a) += F((b), (c), (d)) + (x); \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define GG(a, b, c, d, e, x, s) { \
+ (a) += G((b), (c), (d)) + (x) + 0x5a827999U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define HH(a, b, c, d, e, x, s) { \
+ (a) += H((b), (c), (d)) + (x) + 0x6ed9eba1U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define II(a, b, c, d, e, x, s) { \
+ (a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcU; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define JJ(a, b, c, d, e, x, s) { \
+ (a) += J((b), (c), (d)) + (x) + 0xa953fd4eU; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define FFF(a, b, c, d, e, x, s) { \
+ (a) += F((b), (c), (d)) + (x); \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define GGG(a, b, c, d, e, x, s) { \
+ (a) += G((b), (c), (d)) + (x) + 0x7a6d76e9U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define HHH(a, b, c, d, e, x, s) { \
+ (a) += H((b), (c), (d)) + (x) + 0x6d703ef3U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define III(a, b, c, d, e, x, s) { \
+ (a) += I((b), (c), (d)) + (x) + 0x5c4dd124U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+#define JJJ(a, b, c, d, e, x, s) { \
+ (a) += J((b), (c), (d)) + (x) + 0x50a28be6U; \
+ (a) = ROL((a), (s)) + (e); \
+ (c) = ROL((c), 10); \
+}
+
+
+
+void
+rmd160_init(rmd160_ctx_t *context)
+{
+
+ /* ripemd-160 initialization constants */
+ context->state[0] = 0x67452301U;
+ context->state[1] = 0xefcdab89U;
+ context->state[2] = 0x98badcfeU;
+ context->state[3] = 0x10325476U;
+ context->state[4] = 0xc3d2e1f0U;
+ context->length[0] = context->length[1] = 0;
+ context->buflen = 0;
+}
+
+void
+rmd160_transform(uint32_t state[5], const uint32_t block[16])
+{
+ uint32_t aa, bb, cc, dd, ee;
+ uint32_t aaa, bbb, ccc, ddd, eee;
+
+ aa = aaa = state[0];
+ bb = bbb = state[1];
+ cc = ccc = state[2];
+ dd = ddd = state[3];
+ ee = eee = state[4];
+
+ /* round 1 */
+ FF(aa, bb, cc, dd, ee, block[ 0], 11);
+ FF(ee, aa, bb, cc, dd, block[ 1], 14);
+ FF(dd, ee, aa, bb, cc, block[ 2], 15);
+ FF(cc, dd, ee, aa, bb, block[ 3], 12);
+ FF(bb, cc, dd, ee, aa, block[ 4], 5);
+ FF(aa, bb, cc, dd, ee, block[ 5], 8);
+ FF(ee, aa, bb, cc, dd, block[ 6], 7);
+ FF(dd, ee, aa, bb, cc, block[ 7], 9);
+ FF(cc, dd, ee, aa, bb, block[ 8], 11);
+ FF(bb, cc, dd, ee, aa, block[ 9], 13);
+ FF(aa, bb, cc, dd, ee, block[10], 14);
+ FF(ee, aa, bb, cc, dd, block[11], 15);
+ FF(dd, ee, aa, bb, cc, block[12], 6);
+ FF(cc, dd, ee, aa, bb, block[13], 7);
+ FF(bb, cc, dd, ee, aa, block[14], 9);
+ FF(aa, bb, cc, dd, ee, block[15], 8);
+
+ /* round 2 */
+ GG(ee, aa, bb, cc, dd, block[ 7], 7);
+ GG(dd, ee, aa, bb, cc, block[ 4], 6);
+ GG(cc, dd, ee, aa, bb, block[13], 8);
+ GG(bb, cc, dd, ee, aa, block[ 1], 13);
+ GG(aa, bb, cc, dd, ee, block[10], 11);
+ GG(ee, aa, bb, cc, dd, block[ 6], 9);
+ GG(dd, ee, aa, bb, cc, block[15], 7);
+ GG(cc, dd, ee, aa, bb, block[ 3], 15);
+ GG(bb, cc, dd, ee, aa, block[12], 7);
+ GG(aa, bb, cc, dd, ee, block[ 0], 12);
+ GG(ee, aa, bb, cc, dd, block[ 9], 15);
+ GG(dd, ee, aa, bb, cc, block[ 5], 9);
+ GG(cc, dd, ee, aa, bb, block[ 2], 11);
+ GG(bb, cc, dd, ee, aa, block[14], 7);
+ GG(aa, bb, cc, dd, ee, block[11], 13);
+ GG(ee, aa, bb, cc, dd, block[ 8], 12);
+
+ /* round 3 */
+ HH(dd, ee, aa, bb, cc, block[ 3], 11);
+ HH(cc, dd, ee, aa, bb, block[10], 13);
+ HH(bb, cc, dd, ee, aa, block[14], 6);
+ HH(aa, bb, cc, dd, ee, block[ 4], 7);
+ HH(ee, aa, bb, cc, dd, block[ 9], 14);
+ HH(dd, ee, aa, bb, cc, block[15], 9);
+ HH(cc, dd, ee, aa, bb, block[ 8], 13);
+ HH(bb, cc, dd, ee, aa, block[ 1], 15);
+ HH(aa, bb, cc, dd, ee, block[ 2], 14);
+ HH(ee, aa, bb, cc, dd, block[ 7], 8);
+ HH(dd, ee, aa, bb, cc, block[ 0], 13);
+ HH(cc, dd, ee, aa, bb, block[ 6], 6);
+ HH(bb, cc, dd, ee, aa, block[13], 5);
+ HH(aa, bb, cc, dd, ee, block[11], 12);
+ HH(ee, aa, bb, cc, dd, block[ 5], 7);
+ HH(dd, ee, aa, bb, cc, block[12], 5);
+
+ /* round 4 */
+ II(cc, dd, ee, aa, bb, block[ 1], 11);
+ II(bb, cc, dd, ee, aa, block[ 9], 12);
+ II(aa, bb, cc, dd, ee, block[11], 14);
+ II(ee, aa, bb, cc, dd, block[10], 15);
+ II(dd, ee, aa, bb, cc, block[ 0], 14);
+ II(cc, dd, ee, aa, bb, block[ 8], 15);
+ II(bb, cc, dd, ee, aa, block[12], 9);
+ II(aa, bb, cc, dd, ee, block[ 4], 8);
+ II(ee, aa, bb, cc, dd, block[13], 9);
+ II(dd, ee, aa, bb, cc, block[ 3], 14);
+ II(cc, dd, ee, aa, bb, block[ 7], 5);
+ II(bb, cc, dd, ee, aa, block[15], 6);
+ II(aa, bb, cc, dd, ee, block[14], 8);
+ II(ee, aa, bb, cc, dd, block[ 5], 6);
+ II(dd, ee, aa, bb, cc, block[ 6], 5);
+ II(cc, dd, ee, aa, bb, block[ 2], 12);
+
+ /* round 5 */
+ JJ(bb, cc, dd, ee, aa, block[ 4], 9);
+ JJ(aa, bb, cc, dd, ee, block[ 0], 15);
+ JJ(ee, aa, bb, cc, dd, block[ 5], 5);
+ JJ(dd, ee, aa, bb, cc, block[ 9], 11);
+ JJ(cc, dd, ee, aa, bb, block[ 7], 6);
+ JJ(bb, cc, dd, ee, aa, block[12], 8);
+ JJ(aa, bb, cc, dd, ee, block[ 2], 13);
+ JJ(ee, aa, bb, cc, dd, block[10], 12);
+ JJ(dd, ee, aa, bb, cc, block[14], 5);
+ JJ(cc, dd, ee, aa, bb, block[ 1], 12);
+ JJ(bb, cc, dd, ee, aa, block[ 3], 13);
+ JJ(aa, bb, cc, dd, ee, block[ 8], 14);
+ JJ(ee, aa, bb, cc, dd, block[11], 11);
+ JJ(dd, ee, aa, bb, cc, block[ 6], 8);
+ JJ(cc, dd, ee, aa, bb, block[15], 5);
+ JJ(bb, cc, dd, ee, aa, block[13], 6);
+
+ /* parallel round 1 */
+ JJJ(aaa, bbb, ccc, ddd, eee, block[ 5], 8);
+ JJJ(eee, aaa, bbb, ccc, ddd, block[14], 9);
+ JJJ(ddd, eee, aaa, bbb, ccc, block[ 7], 9);
+ JJJ(ccc, ddd, eee, aaa, bbb, block[ 0], 11);
+ JJJ(bbb, ccc, ddd, eee, aaa, block[ 9], 13);
+ JJJ(aaa, bbb, ccc, ddd, eee, block[ 2], 15);
+ JJJ(eee, aaa, bbb, ccc, ddd, block[11], 15);
+ JJJ(ddd, eee, aaa, bbb, ccc, block[ 4], 5);
+ JJJ(ccc, ddd, eee, aaa, bbb, block[13], 7);
+ JJJ(bbb, ccc, ddd, eee, aaa, block[ 6], 7);
+ JJJ(aaa, bbb, ccc, ddd, eee, block[15], 8);
+ JJJ(eee, aaa, bbb, ccc, ddd, block[ 8], 11);
+ JJJ(ddd, eee, aaa, bbb, ccc, block[ 1], 14);
+ JJJ(ccc, ddd, eee, aaa, bbb, block[10], 14);
+ JJJ(bbb, ccc, ddd, eee, aaa, block[ 3], 12);
+ JJJ(aaa, bbb, ccc, ddd, eee, block[12], 6);
+
+ /* parallel round 2 */
+ III(eee, aaa, bbb, ccc, ddd, block[ 6], 9);
+ III(ddd, eee, aaa, bbb, ccc, block[11], 13);
+ III(ccc, ddd, eee, aaa, bbb, block[ 3], 15);
+ III(bbb, ccc, ddd, eee, aaa, block[ 7], 7);
+ III(aaa, bbb, ccc, ddd, eee, block[ 0], 12);
+ III(eee, aaa, bbb, ccc, ddd, block[13], 8);
+ III(ddd, eee, aaa, bbb, ccc, block[ 5], 9);
+ III(ccc, ddd, eee, aaa, bbb, block[10], 11);
+ III(bbb, ccc, ddd, eee, aaa, block[14], 7);
+ III(aaa, bbb, ccc, ddd, eee, block[15], 7);
+ III(eee, aaa, bbb, ccc, ddd, block[ 8], 12);
+ III(ddd, eee, aaa, bbb, ccc, block[12], 7);
+ III(ccc, ddd, eee, aaa, bbb, block[ 4], 6);
+ III(bbb, ccc, ddd, eee, aaa, block[ 9], 15);
+ III(aaa, bbb, ccc, ddd, eee, block[ 1], 13);
+ III(eee, aaa, bbb, ccc, ddd, block[ 2], 11);
+
+ /* parallel round 3 */
+ HHH(ddd, eee, aaa, bbb, ccc, block[15], 9);
+ HHH(ccc, ddd, eee, aaa, bbb, block[ 5], 7);
+ HHH(bbb, ccc, ddd, eee, aaa, block[ 1], 15);
+ HHH(aaa, bbb, ccc, ddd, eee, block[ 3], 11);
+ HHH(eee, aaa, bbb, ccc, ddd, block[ 7], 8);
+ HHH(ddd, eee, aaa, bbb, ccc, block[14], 6);
+ HHH(ccc, ddd, eee, aaa, bbb, block[ 6], 6);
+ HHH(bbb, ccc, ddd, eee, aaa, block[ 9], 14);
+ HHH(aaa, bbb, ccc, ddd, eee, block[11], 12);
+ HHH(eee, aaa, bbb, ccc, ddd, block[ 8], 13);
+ HHH(ddd, eee, aaa, bbb, ccc, block[12], 5);
+ HHH(ccc, ddd, eee, aaa, bbb, block[ 2], 14);
+ HHH(bbb, ccc, ddd, eee, aaa, block[10], 13);
+ HHH(aaa, bbb, ccc, ddd, eee, block[ 0], 13);
+ HHH(eee, aaa, bbb, ccc, ddd, block[ 4], 7);
+ HHH(ddd, eee, aaa, bbb, ccc, block[13], 5);
+
+ /* parallel round 4 */
+ GGG(ccc, ddd, eee, aaa, bbb, block[ 8], 15);
+ GGG(bbb, ccc, ddd, eee, aaa, block[ 6], 5);
+ GGG(aaa, bbb, ccc, ddd, eee, block[ 4], 8);
+ GGG(eee, aaa, bbb, ccc, ddd, block[ 1], 11);
+ GGG(ddd, eee, aaa, bbb, ccc, block[ 3], 14);
+ GGG(ccc, ddd, eee, aaa, bbb, block[11], 14);
+ GGG(bbb, ccc, ddd, eee, aaa, block[15], 6);
+ GGG(aaa, bbb, ccc, ddd, eee, block[ 0], 14);
+ GGG(eee, aaa, bbb, ccc, ddd, block[ 5], 6);
+ GGG(ddd, eee, aaa, bbb, ccc, block[12], 9);
+ GGG(ccc, ddd, eee, aaa, bbb, block[ 2], 12);
+ GGG(bbb, ccc, ddd, eee, aaa, block[13], 9);
+ GGG(aaa, bbb, ccc, ddd, eee, block[ 9], 12);
+ GGG(eee, aaa, bbb, ccc, ddd, block[ 7], 5);
+ GGG(ddd, eee, aaa, bbb, ccc, block[10], 15);
+ GGG(ccc, ddd, eee, aaa, bbb, block[14], 8);
+
+ /* parallel round 5 */
+ FFF(bbb, ccc, ddd, eee, aaa, block[12] , 8);
+ FFF(aaa, bbb, ccc, ddd, eee, block[15] , 5);
+ FFF(eee, aaa, bbb, ccc, ddd, block[10] , 12);
+ FFF(ddd, eee, aaa, bbb, ccc, block[ 4] , 9);
+ FFF(ccc, ddd, eee, aaa, bbb, block[ 1] , 12);
+ FFF(bbb, ccc, ddd, eee, aaa, block[ 5] , 5);
+ FFF(aaa, bbb, ccc, ddd, eee, block[ 8] , 14);
+ FFF(eee, aaa, bbb, ccc, ddd, block[ 7] , 6);
+ FFF(ddd, eee, aaa, bbb, ccc, block[ 6] , 8);
+ FFF(ccc, ddd, eee, aaa, bbb, block[ 2] , 13);
+ FFF(bbb, ccc, ddd, eee, aaa, block[13] , 6);
+ FFF(aaa, bbb, ccc, ddd, eee, block[14] , 5);
+ FFF(eee, aaa, bbb, ccc, ddd, block[ 0] , 15);
+ FFF(ddd, eee, aaa, bbb, ccc, block[ 3] , 13);
+ FFF(ccc, ddd, eee, aaa, bbb, block[ 9] , 11);
+ FFF(bbb, ccc, ddd, eee, aaa, block[11] , 11);
+
+ /* combine results */
+ ddd += cc + state[1]; /* final result for state[0] */
+ state[1] = state[2] + dd + eee;
+ state[2] = state[3] + ee + aaa;
+ state[3] = state[4] + aa + bbb;
+ state[4] = state[0] + bb + ccc;
+ state[0] = ddd;
+}
+
+void
+rmd160_update(rmd160_ctx_t *context, const uint8_t *data, uint32_t nbytes)
+{
+ uint32_t X[16];
+ uint32_t ofs = 0;
+ uint32_t i;
+#if BYTE_ORDER != LITTLE_ENDIAN
+ uint32_t j;
+#endif
+
+ /* update length[] */
+ if (context->length[0] + nbytes < context->length[0])
+ context->length[1]++; /* overflow to msb of length */
+ context->length[0] += nbytes;
+
+ (void) memset(X, 0, sizeof(X));
+
+ if (context->buflen + nbytes < 64) {
+ (void) memcpy(context->bbuffer + context->buflen, data,
+ nbytes);
+ context->buflen += nbytes;
+ } else {
+ /* process first block */
+ ofs = 64 - context->buflen;
+ (void) memcpy(context->bbuffer + context->buflen, data, ofs);
+#if BYTE_ORDER == LITTLE_ENDIAN
+ (void) memcpy(X, context->bbuffer, sizeof(X));
+#else
+ for (j = 0; j < 16; j++)
+ X[j] = BYTES_TO_DWORD(context->bbuffer + (4 * j));
+#endif
+ rmd160_transform(context->state, X);
+ nbytes -= ofs;
+
+ /* process remaining complete blocks */
+ for (i = 0; i < (nbytes >> 6); i++) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ (void) memcpy(X, data + (64 * i) + ofs, sizeof(X));
+#else
+ for (j = 0; j < 16; j++)
+ X[j] = BYTES_TO_DWORD(data + (64 * i) +
+ (4 * j) + ofs);
+#endif
+ rmd160_transform(context->state, X);
+ }
+
+ /*
+ * Put last bytes from data into context's buffer
+ */
+ context->buflen = nbytes & 63;
+ (void) memcpy(context->bbuffer, data + (64 * i) + ofs,
+ context->buflen);
+ }
+}
+
+void
+rmd160_final(uint8_t digest[20], rmd160_ctx_t *context)
+{
+ uint32_t i;
+ uint32_t X[16];
+#if BYTE_ORDER != LITTLE_ENDIAN
+ uint32_t j;
+#endif
+
+ /* append the bit m_n == 1 */
+ context->bbuffer[context->buflen] = (uint8_t)'\200';
+
+ (void) memset(context->bbuffer + context->buflen + 1, 0,
+ 63 - context->buflen);
+#if BYTE_ORDER == LITTLE_ENDIAN
+ (void) memcpy(X, context->bbuffer, sizeof(X));
+#else
+ for (j = 0; j < 16; j++)
+ X[j] = BYTES_TO_DWORD(context->bbuffer + (4 * j));
+#endif
+ if ((context->buflen) > 55) {
+ /* length goes to next block */
+ rmd160_transform(context->state, X);
+ (void) memset(X, 0, sizeof(X));
+ }
+
+ /* append length in bits */
+ X[14] = context->length[0] << 3;
+ X[15] = (context->length[0] >> 29) | (context->length[1] << 3);
+ rmd160_transform(context->state, X);
+
+ for (i = 0; i < 20; i += 4) {
+ /* extracts the 8 least significant bits. */
+ digest[i] = context->state[i>>2];
+ digest[i + 1] = (context->state[i>>2] >> 8);
+ digest[i + 2] = (context->state[i>>2] >> 16);
+ digest[i + 3] = (context->state[i>>2] >> 24);
+ }
+}
--- /dev/null
+/* $Id: rmd160.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: rmd160.h,v 1.2 2000/07/07 10:47:06 ad Exp $ */
+
+/*
+ * ANSIfied and cleaned up by Matthew Mondor, 2006
+ */
+
+/********************************************************************\
+ *
+ * FILE: rmd160.h
+ *
+ * CONTENTS: Header file for a sample C-implementation of the
+ * RIPEMD-160 hash-function.
+ * TARGET: any computer with an ANSI C compiler
+ *
+ * AUTHOR: Antoon Bosselaers, ESAT-COSIC
+ * DATE: 1 March 1996
+ * VERSION: 1.0
+ *
+ * Copyright (c) Katholieke Universiteit Leuven
+ * 1996, All Rights Reserved
+ *
+\********************************************************************/
+
+/*
+ * from OpenBSD: rmd160.h,v 1.4 1999/08/16 09:59:04 millert Exp
+ */
+
+#ifndef _RMD160_H_
+#define _RMD160_H_
+
+
+
+#include <stdint.h>
+
+
+
+#define RMD160_DIGEST_LENGTH 20
+
+typedef struct {
+ uint32_t state[5]; /* state (ABCDE) */
+ uint32_t length[2]; /* number of bits */
+ uint8_t bbuffer[64]; /* overflow buffer */
+ uint32_t buflen; /* number of chars in bbuffer */
+} rmd160_ctx_t;
+
+
+
+void rmd160_init(rmd160_ctx_t *);
+void rmd160_tansform(uint32_t[5], const uint32_t[16]);
+void rmd160_update(rmd160_ctx_t *, const uint8_t *, uint32_t);
+void rmd160_final(uint8_t[20], rmd160_ctx_t *);
+
+
+
+#endif /* !_RMD160_H_ */
--- /dev/null
+/* $Id: sha1.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: sha1.c,v 1.12 2003/10/27 00:12:42 lukem Exp $ */
+/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */
+
+/*
+ * ANSIfied and cleaned up by Matthew Mondor, 2006
+ */
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+
+
+
+#include <stdint.h>
+#include <string.h>
+
+#ifdef __NetBSD__
+#include <machine/endian.h>
+#endif
+
+#include <sha1.h>
+
+
+
+#ifndef BYTE_ORDER
+#error "BYTEORDER undefined!"
+#endif
+
+
+
+#define SHA1HANDSOFF /* Copies data before messing with it. */
+
+#define _DIAGASSERT(x) (void)0
+
+#define rol(value, bits) \
+ (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/*
+ * blk0() and blk() perform the initial expand.
+ * I got the idea of expanding during the round function from SSLeay
+ */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) \
+ (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+ (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) \
+ (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ \
+ block->l[ i & 15], 1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ */
+#define R0(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R1(v, w, x, y, z, i) \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30);
+#define R2(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v, w, x, y, z, i) \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30);
+#define R4(v, w, x, y, z, i) \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30);
+
+typedef union {
+ uint8_t c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+
+
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+void
+sha1_transform(uint32_t state[5], const uint8_t buffer[64])
+{
+ uint32_t a, b, c, d, e;
+ CHAR64LONG16 *block;
+
+#ifdef SHA1HANDSOFF
+ CHAR64LONG16 workspace;
+#endif
+
+#ifdef SHA1HANDSOFF
+ block = &workspace;
+ (void) memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16 *)(void *)buffer;
+#endif
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0);
+ R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2);
+ R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4);
+ R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6);
+ R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8);
+ R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10);
+ R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12);
+ R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14);
+ R0(a, b, c, d, e, 15);
+ R1(e, a, b, c, d, 16);
+ R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18);
+ R1(b, c, d, e, a, 19);
+ R2(a, b, c, d, e, 20);
+ R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22);
+ R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24);
+ R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26);
+ R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28);
+ R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30);
+ R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32);
+ R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34);
+ R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36);
+ R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38);
+ R2(b, c, d, e, a, 39);
+ R3(a, b, c, d, e, 40);
+ R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42);
+ R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44);
+ R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46);
+ R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48);
+ R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50);
+ R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52);
+ R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54);
+ R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56);
+ R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58);
+ R3(b, c, d, e, a, 59);
+ R4(a, b, c, d, e, 60);
+ R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62);
+ R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64);
+ R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66);
+ R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68);
+ R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70);
+ R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72);
+ R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74);
+ R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76);
+ R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78);
+ R4(b, c, d, e, a, 79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+/*
+ * Initialize new context
+ */
+void
+sha1_init(sha1_ctx_t *context)
+{
+
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+/*
+ * Run your data through this.
+ */
+void
+sha1_update(sha1_ctx_t *context, const uint8_t *data, uint32_t len)
+{
+ uint32_t i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1] += (len >> 29) + 1;
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ (void) memcpy(&context->buffer[j], data, (i = 64 - j));
+ sha1_transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64)
+ sha1_transform(context->state, &data[i]);
+ j = 0;
+ } else
+ i = 0;
+ (void) memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+/*
+ * Add padding and return the message digest.
+ */
+void
+sha1_final(uint8_t digest[20], sha1_ctx_t *context)
+{
+ unsigned int i;
+ uint8_t finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ /* Endian independent */
+ finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3 - (i & 3)) * 8)) & 255);
+ }
+ sha1_update(context, (uint8_t *)"\200", 1);
+ while ((context->count[0] & 504) != 448)
+ sha1_update(context, (uint8_t *) "\0", 1);
+ /* Should cause an sha1_transform() */
+ sha1_update(context, finalcount, 8);
+
+ for (i = 0; i < 20; i++)
+ digest[i] = (uint8_t)((context->state[i >> 2] >>
+ ((3 - (i & 3)) * 8)) & 255);
+}
--- /dev/null
+/* $Id: sha1.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+/* $NetBSD: sha1.h,v 1.5.2.1 2005/06/10 14:38:35 tron Exp $ */
+
+/*
+ * ANSIfied and cleaned up by Matthew Mondor, 2006
+ */
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+
+
+#include <stdint.h>
+
+
+
+#define SHA1_DIGEST_LENGTH 20
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ uint8_t buffer[64];
+} sha1_ctx_t;
+
+
+
+void sha1_transform(uint32_t[5], const uint8_t[64]);
+void sha1_init(sha1_ctx_t *);
+void sha1_update(sha1_ctx_t *, const uint8_t *, uint32_t);
+void sha1_final(uint8_t[SHA1_DIGEST_LENGTH], sha1_ctx_t *);
+
+
+
+#endif
--- /dev/null
+# $Id: GNUmakefile,v 1.1 2006/12/31 08:32:40 mmondor Exp $
+
+MMLIB_PATH := ../../../mmlib
+
+MMLIBS := $(addprefix $(MMLIB_PATH)/,mmpool.o mmstring.o mmarch.o)
+LIBS := -lc -lm -lz
+OBJS := $(addprefix src/,main.o trigonometry.o net.o kqueue.o sendq.o recvq.o \
+ packets.o daemon.o client.o ships.o torp.o enc.o) \
+ $(addprefix ../common/,hmac_sha1.o hmac_rmd160.o sha1.o rmd160.o \
+ mmenc.o)
+CFLAGS += -Wall -g
+BINS := tms-server
+
+all: $(BINS)
+
+%.o: %.c
+ cc -c $(CFLAGS) -Isrc -I../common -I$(MMLIB_PATH) -o $@ $<
+
+
+tms-server: $(MMLIBS) $(LIBS) $(OBJS)
+ cc -o $@ $(MMLIBS) $(OBJS) $(LIBS)
+
+
+clean:
+ rm -f $(BINS) $(OBJS) $(MMLIBS) $(LIBS)
--- /dev/null
+$Id: README,v 1.1 2006/12/31 08:32:40 mmondor Exp $
+
+If this all works fine, it might be the basis of a multiplayer
+networking game. However, this is primarily a test to learn kqueue
+and observe its efficiency, as well as to experiment using TCP for
+such massive multiplayer games on today's networks and internet.
+
+Many games that used TCP for LAN use a while back had to develop
+UDP based protocols to be efficient enough for dialup users or
+other internet users. We will determine if this is still necessary
+on today's average internet latencies.
+
+We probably still want to prioritize some packets over others.
+For instance, we might drop outgoing position update packets which
+cannot be sent immediately instead of queueing them, allowing less
+frequent updates to slow connections while not needing to provide
+multiple independent game heart rates.
+
+We do not expect to have to port the server part of the game, wich
+is intended to run on BSD systems supporting the kqueue(2) system
+only. We could have used an event library, however we wanted to
+avoid including non-BSD licensed components. Although there is a
+BSD licensed libevent as part of NetBSD, using kqueue directly
+allows a few fancy optimization tricks which would require a new
+portable events library to be designed to allow to achieve the full
+advantages of kqueue using an abstracted library.
+
+The client code, however, will be portable and should be able to
+run on Win32 without needing the cygwin runtime libraries. The
+SDL dependency is reasonable, and can be provided separately, so
+mingw will be used to create those executables. The client will
+however primarily be developped on NetBSD. Like SDL, OpenGL
+libraries shouldn't be a problem either.
+
+To avoid using cygwin, and because Win32 winsock does not conform
+to BSD sockets API standards, we expect to have to write a simple
+portable networking layer for use in the client.
+
+
+TODO
+
+- At this point to advance further a client needs to be written.
+- We probably don't need to align packets, we only need to make sure
+ that their size is 32-bit aligned, and that their 16-bit fields are
+ 16-bit aligned, their 32-bit ones 32-bit aligned. Packet type field
+ can be 8-bit. If we allowed arbitrary offsets when buffering packets,
+ we would need to copy each packet to an aligned buffer as we process them.
+ Evaluate which is more advantageous.
--- /dev/null
+/* $Id: client.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <client.h>
+#include <kqueue.h>
+#include <conf.h>
+
+
+
+static bool client_constructor(pnode_t *);
+static void client_destructor(pnode_t *);
+
+
+
+static list_t clients_gc_list;
+static pool_t clients_pool;
+
+
+
+list_t clients_list;
+
+
+
+void
+client_init(void)
+{
+
+ DLIST_INIT(&clients_list);
+ DLIST_INIT(&clients_gc_list);
+
+ if (!pool_init(&clients_pool, "clients_pool", malloc, free,
+ client_constructor, client_destructor, sizeof(client_t),
+ 8, 1, MAX_CLIENTS / 8)) {
+ syslog(LOG_NOTICE, "pool_init() - %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+}
+
+/*
+ * Enables the write polling filter for the specified client.
+ */
+void
+client_enable_write_polling(void *args)
+{
+ client_t *c = (client_t *)args;
+
+ if (c->writepolling)
+ return;
+
+ kqueue_sev_alloc(1);
+ kqueue_sev_add(c->fd, EVFILT_WRITE, EV_ENABLE, 0, 0, (intptr_t)c);
+
+ c->writepolling = 1;
+}
+
+inline int
+client_write(client_t *c, uint8_t *buf, size_t size, int buffer)
+{
+
+ if (sendq_write(&c->sendq, buf, size, client_enable_write_polling, c,
+ buffer) == -1) {
+ syslog(LOG_NOTICE, "sendq_write(%u) - %s", size,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * XXX We should do connection rate/concurrency checking on address here.
+ */
+client_t *
+client_create(int fd, struct sockaddr *saddr)
+{
+ client_t *c;
+
+ if (DLIST_NODES(&clients_list) == MAX_CLIENTS) {
+ syslog(LOG_NOTICE, "client_create() - MAX_CLIENTS reached");
+ return NULL;
+ }
+
+ if ((c = (client_t *)pool_alloc(&clients_pool, FALSE)) != NULL) {
+ c->fd = fd;
+ sendq_reset(&c->sendq, c);
+ recvq_reset(&c->recvq, c);
+ (void) memcpy(&c->saddr, saddr, sizeof(struct sockaddr));
+
+ ship_init(&c->ship, SHIP_CA);
+
+ c->authenticated = c->todestroy = c->toclose = 0;
+ c->writepolling = 1;
+ c->askedping = 0;
+ c->slowlevel = c->slowcnt = 0;
+#ifdef USE_ENCRYPTION
+ c->mmencrypt = 0;
+#endif
+ c->created = current_time;
+
+ DLIST_APPEND(&clients_list, (node_t *)c);
+ } else
+ syslog(LOG_NOTICE, "client_create() - pool_alloc() - %s",
+ strerror(errno));
+
+ return c;
+}
+
+void
+client_destroy_mark(client_t *c)
+{
+
+ if (c->todestroy)
+ return;
+
+ c->todestroy = 1;
+ DLIST_SWAP(&clients_gc_list, &clients_list, (node_t *)c, FALSE);
+}
+
+void
+client_destroy_marked(void)
+{
+ client_t *c, *n;
+
+ for (c = (client_t *)DLIST_TOP(&clients_gc_list); c != NULL; c = n) {
+ n = (client_t *)DLIST_NEXT((node_t *)c);
+
+ /*
+ * Delay destruction of client structure until all torpidoes
+ * expired, since torpidoes access client structure data.
+ * XXX This is extremely hackish, and it wastes a lot of
+ * CPU time!
+ */
+ if (c->ship.torps > 0)
+ continue;
+
+ /* Closing descriptor automatically deletes its kevents */
+ (void) close(c->fd);
+
+#ifdef USE_ENCRYPTION
+ /* Make sure to destroy cryptographic keys information */
+ (void) memset(&c->recvq.enc_in, '\0', sizeof(mmenc_t));
+ (void) memset(&c->sendq.enc_out, '\0', sizeof(mmenc_t));
+#endif
+
+ DLIST_UNLINK(&clients_gc_list, (node_t *)c);
+ pool_free((pnode_t *)c);
+ }
+}
+
+/*
+ * Runs through list of clients and verify if the number of received packets
+ * exceeds zero, unless the client was too recently created. Those with input
+ * timeout are to be destroyed.
+ */
+void
+client_timeout(void)
+{
+ client_t *c;
+
+ /*
+ * We can safely use DLIST_FOREACH() since client_destroy_mark()
+ * doesn't yet destroy the node, and we're single-threaded.
+ */
+ DLIST_FOREACH(&clients_list, (client_t *)c) {
+ if (current_time - c->created >= TIMEOUT_SECONDS &&
+ c->recvq.recvpackets == 0)
+ client_destroy_mark(c);
+ c->recvq.recvpackets = 0;
+ }
+}
+
+static bool
+client_constructor(pnode_t *n)
+{
+ client_t *c = (client_t *)n;
+ int sendq = 0;
+
+ if (sendq_init(&c->sendq, SENDQ_SIZE) != 0) {
+ syslog(LOG_NOTICE,
+ "client_constructor() - sendq_init() - %s",
+ strerror(errno));
+ goto err;
+ }
+ sendq = 1;
+
+ if (recvq_init(&c->recvq, RECVQ_SIZE) != 0) {
+ syslog(LOG_NOTICE,
+ "client_constructor() - recvq_init() - %s",
+ strerror(errno));
+ goto err;
+ }
+
+ return TRUE;
+
+err:
+ if (sendq)
+ sendq_destroy(&c->sendq);
+
+ return FALSE;
+}
+
+static void
+client_destructor(pnode_t *n)
+{
+ client_t *c = (client_t *)n;
+
+ recvq_destroy(&c->recvq);
+ sendq_destroy(&c->sendq);
+}
--- /dev/null
+/* $Id: client.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _CLIENT_H_
+#define _CLIENT_H_
+
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <time.h>
+
+#include <mmpool.h>
+#include <mmlist.h>
+
+#include <sendq.h>
+#include <recvq.h>
+#include <ships.h>
+#include <conf.h>
+
+
+
+typedef struct client {
+ pnode_t node;
+ ship_t ship;
+ struct sockaddr saddr;
+ int fd;
+ sendq_t sendq;
+ recvq_t recvq;
+ int authenticated, todestroy, toclose, writepolling,
+ askedping, slowlevel, slowcnt;
+#ifdef USE_ENCRYPTION
+ int mmencrypt;
+#endif
+ time_t created;
+ uint32_t noncerand1[8], noncerand2[8];
+} client_t;
+
+
+
+void client_init(void);
+void client_enable_write_polling(void *);
+inline int client_write(client_t *, u_int8_t *, size_t, int);
+
+client_t *client_create(int, struct sockaddr *);
+void client_destroy_mark(client_t *);
+void client_destroy_marked(void);
+
+void client_timeout(void);
+
+
+
+extern list_t clients_list;
+
+
+
+#endif
--- /dev/null
+/* $Id: conf.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _CONF_H_
+#define _CONF_H_
+
+
+
+#define DAEMON_NODETACH 0
+
+#define SERVER_VERSION 1
+#define SERVER_STRING "tms-server/mmondor\n"
+
+#define FPS 20
+#define FPS_MS (1000 / FPS)
+#define TIMEOUT_SECONDS 30
+
+#define WORLD_X_MAX 640
+#define WORLD_Y_MAX 480
+
+#define MAX_CLIENTS 128
+#define R_EVENTS (MAX_CLIENTS * 2)
+#define S_EVENTS (MAX_CLIENTS * 2)
+
+#define SENDQ_SIZE 16384
+#define RECVQ_SIZE 1024
+
+#define PIDFILE "/tmp/daemon.pid"
+#define RNDFILE "/dev/urandom"
+
+#define SERVER_LOGIN "login"
+#define SERVER_PASSWD "d9d19c4285a4782a1b231d98aff232a046978cc2"
+
+#define USE_COMPRESSION
+#define USE_ENCRYPTION
+
+
+
+#endif
--- /dev/null
+/* $Id: daemon.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Procedure for becoming an detached daemon, as well as to prepare process
+ * signal handling. Since we'll be catching signal events through kqueue(2),
+ * We simply ignore all signals for which we don't want the default behavior.
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <mmstring.h>
+
+#include <conf.h>
+
+
+
+static int signal_init(void);
+static void pidfile_write(const char *);
+
+
+
+static int
+signal_init(void)
+{
+ struct sigaction act;
+ int i;
+ int sigs[] = {
+ SIGHUP,
+ SIGINT,
+ SIGPIPE,
+ SIGALRM,
+ SIGTERM,
+ SIGTTIN,
+ SIGTTOU,
+ SIGIO,
+ SIGXCPU,
+ SIGXFSZ,
+ SIGVTALRM,
+ SIGPROF,
+ SIGUSR1,
+ SIGUSR2
+ };
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = SA_NOCLDSTOP;
+ (void) sigemptyset(&act.sa_mask);
+
+ for (i = 0; i < (sizeof(sigs) / sizeof(int)); i++) {
+ if (sigaction(sigs[i], &act, NULL) != 0) {
+ syslog(LOG_NOTICE,
+ "signal_init() - sigaction(%d) - %s",
+ sigs[i], strerror(errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Writes our process ID number to specified file. To be called before
+ * chroot(2) or dropping privileges.
+ */
+static void
+pidfile_write(const char *file)
+{
+ char str[16];
+ int fd;
+
+ if ((fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
+ (void) snprintf(str, 15, "%d\n", getpid());
+ (void) write(fd, str, mm_strlen(str));
+ (void) close(fd);
+ } else
+ syslog(LOG_NOTICE, "pidfile_write() - open(%s) - %s",
+ file, strerror(errno));
+}
+
+int
+daemon_init(int nodetach)
+{
+ pid_t pid;
+ int fd;
+
+ /* Create new process */
+ if (!nodetach) {
+ if ((pid = fork()) == -1) {
+ syslog(LOG_NOTICE, "fork() - %s", strerror(errno));
+ return -1;
+ }
+ if (pid != 0)
+ exit(EXIT_SUCCESS);
+ }
+
+ pidfile_write(PIDFILE);
+
+ /* Create new process group and detach */
+ if (!nodetach) {
+ (void) setsid();
+ (void) chdir("/");
+ if ((fd = open("/dev/null", O_RDWR)) != -1) {
+ (void) dup2(fd, STDIN_FILENO);
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ (void) close(fd);
+ } else
+ syslog(LOG_NOTICE,
+ "daemon_init() - open(/dev/null) - %s",
+ strerror(errno));
+ }
+
+ if (signal_init() != 0)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/* $Id: daemon.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _DAEMON_H_
+#define _DAEMON_H_
+
+
+
+int daemon_init(int);
+
+
+
+#endif
--- /dev/null
+/* $Id: enc.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mmarch.h>
+
+#include <enc.h>
+#include <mmenc.h>
+#include <kqueue.h> /* current_time */
+#include <conf.h>
+
+
+
+static inline uint32_t noncerand_rnd(void);
+static inline void noncerand_seed(void);
+
+
+
+static uint32_t rndcnt;
+static mmenc_t prng;
+static int fd;
+
+
+
+/*
+ * Open random device, initialize PRNG and rndcnt.
+ */
+void
+noncerand_init(void)
+{
+ uint32_t key[128];
+
+ if ((fd = open(RNDFILE, O_RDONLY)) == -1) {
+ syslog(LOG_NOTICE,
+ "noncerand_init() - open(%s) - %s",
+ RNDFILE, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (read(fd, key, sizeof(key)) != sizeof(key)) {
+ syslog(LOG_NOTICE,
+ "noncerand_init() - read() - %s",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ mmenc_init(&prng, (uint8_t *)key, sizeof(key));
+
+ rndcnt = noncerand_rnd();
+}
+
+/* XXX Make more efficient eventually */
+void
+noncerand_gen(uint32_t tnr[8], uint8_t nr[32])
+{
+
+ noncerand_seed();
+ rndcnt += ((1 + noncerand_rnd()) % 32);
+ tnr[0] = BYTEORDER_NETWORK32(current_time);
+ tnr[1] = BYTEORDER_NETWORK32(rndcnt);
+ tnr[2] = BYTEORDER_NETWORK32(noncerand_rnd());
+ tnr[3] = BYTEORDER_NETWORK32(noncerand_rnd());
+ tnr[4] = BYTEORDER_NETWORK32(noncerand_rnd());
+ tnr[5] = BYTEORDER_NETWORK32(noncerand_rnd());
+ tnr[6] = BYTEORDER_NETWORK32(noncerand_rnd());
+ tnr[7] = BYTEORDER_NETWORK32(noncerand_rnd());
+ (void) memcpy(nr, tnr, 32);
+}
+
+static inline uint32_t
+noncerand_rnd(void)
+{
+ uint32_t r;
+
+ mmenc_random(&prng, (uint8_t *)&r, sizeof(r));
+
+ return r;
+}
+
+static inline void
+noncerand_seed(void)
+{
+ uint32_t rs;
+
+ if (read(fd, &rs, sizeof(rs)) != sizeof(rs))
+ syslog(LOG_NOTICE, "noncerand_seed() - read() - %s",
+ strerror(errno));
+ else
+ mmenc_seed(&prng, (uint8_t *)&rs, sizeof(rs));
+}
--- /dev/null
+/* $Id: enc.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+#ifndef _ENC_H_
+#define _ENC_H_
+
+
+
+#include <stdint.h>
+
+
+
+void noncerand_init(void);
+void noncerand_gen(uint32_t tnr[8], uint8_t nr[32]);
+
+
+
+#endif
--- /dev/null
+/* $Id: kqueue.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <mmarch.h>
+
+#include <kqueue.h>
+#include <client.h>
+#include <packets_common.h>
+#include <packets.h>
+#include <net.h>
+#include <conf.h>
+
+
+
+static struct kevent rev[R_EVENTS], sev[S_EVENTS];
+static int kqid, rev_cnt, sev_cnt;
+
+
+
+/*
+ * Optimization to only need to query the current time once per second for the
+ * whole application.
+ */
+time_t current_time;
+
+
+
+/* The following functions simply exit on failure, because they are critical */
+
+void
+kqueue_init(void)
+{
+
+ if ((kqid = kqueue()) == -1) {
+ syslog(LOG_NOTICE, "kqueue_init() - kqueue() - %s",
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ sev_cnt = 0;
+ current_time = time(NULL);
+}
+
+void
+kqueue_addlisten(int fd)
+{
+
+ EV_SET(sev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (intptr_t)NULL);
+ if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
+ syslog(LOG_NOTICE, "kqueue_addlisten() - kevent(%d) - %s",
+ fd, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+}
+
+void
+kqueue_addsignal(int sig)
+{
+
+ EV_SET(sev, sig, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+ (intptr_t)NULL);
+ if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
+ syslog(LOG_NOTICE, "kqueue_addsignal() - kevent(%d) - %s",
+ sig, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void
+kqueue_addtimer(int id, int64_t ms)
+{
+
+ EV_SET(sev, id, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, ms,
+ (intptr_t)NULL);
+ if (kevent(kqid, sev, 1, NULL, 0, NULL) == -1) {
+ syslog(LOG_NOTICE, "kevent_addtimer(%d) - kevent(%lld) - %s",
+ id, ms, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+void
+kqueue_main(void)
+{
+
+ for (;;) {
+ int e, i;
+ struct kevent *kev;
+ client_t *c;
+
+ /*
+ * Fusion to only require one kevent(2) call if there are
+ * events to send. Wait for events to occur.
+ * This also allows us to catch error events.
+ */
+ if ((rev_cnt = kevent(kqid, sev, sev_cnt, rev, R_EVENTS, NULL))
+ == -1) {
+ syslog(LOG_NOTICE, "kevent(1) - %s", strerror(errno));
+ continue;
+ }
+ sev_cnt = 0;
+
+ /* Run through received events and process them */
+ for (e = 0; e < rev_cnt; e++) {
+ kev = &rev[e];
+
+ /* Report errors if any */
+ if ((kev->flags & EV_ERROR) != 0) {
+ syslog(LOG_NOTICE,
+ "EV_ERROR: ident=%d filter=%d flags=%d "
+ "fflags=%d data=%lld udata=%p",
+ kev->ident, kev->filter, kev->flags,
+ kev->fflags, kev->data,
+ (void *)kev->udata);
+ continue;
+ }
+
+ /* Process signals if any */
+ if (kev->filter == EVFILT_SIGNAL) {
+ switch (kev->ident) {
+ case SIGTERM:
+ syslog(LOG_NOTICE,
+ "Received SIGTERM, exiting");
+ exit(EXIT_SUCCESS);
+ break;
+ case SIGPIPE:
+ /* XXX */
+ syslog(LOG_NOTICE,
+ "Received SIGPIPE!");
+ client_destroy_marked();
+ break;
+ }
+ continue;
+ }
+
+ /* Process timeouts if any */
+ if (kev->filter == EVFILT_TIMER) {
+ switch (kev->ident) {
+ case TIMER_FPS:
+ packets_update();
+ break;
+ case TIMER_SECOND:
+ current_time = time(NULL);
+ break;
+ case TIMER_TIMEOUT:
+ client_timeout();
+ break;
+ }
+ continue;
+ }
+
+ if (kev->ident == listen_fd) {
+ int t;
+
+ /*
+ * Accept new connections, create clients and
+ * add new descriptors to the kqueue set,
+ * buffering them to minimize syscalls.
+ */
+ for (i = 0, t = kev->data; i < t; i++) {
+ struct sockaddr saddr;
+ socklen_t saddrl;
+ client_t *c;
+ int fd;
+
+ saddrl = sizeof(struct sockaddr);
+ if ((fd = accept(listen_fd, &saddr,
+ &saddrl)) == -1)
+ continue;
+
+ if ((c = client_create(fd, &saddr))
+ == NULL) {
+ (void) close(fd);
+ continue;
+ }
+
+ kqueue_sev_alloc(2);
+ kqueue_sev_add(fd, EVFILT_READ,
+ EV_ADD | EV_EOF | EV_ENABLE, 0, 0,
+ (intptr_t)c);
+ kqueue_sev_add(fd, EVFILT_WRITE,
+ EV_ADD | EV_EOF | EV_ENABLE, 0, 0,
+ (intptr_t)c);
+
+ /* Send auth request packet */
+ if (spacket_auth_send(c) == -1) {
+ client_destroy_mark(c);
+ continue;
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Don't process any more events for marked to be
+ * destroyed clients, or those without an associated
+ * udata. When client descriptor must be closed and
+ * client structure freed, we make sure to first mark
+ * it as to destroy within this loop, since it's
+ * possible for more than one event to occur for a
+ * single descriptor. We use client_destroy_mark()
+ * for this purpose.
+ */
+ if ((c = (client_t *)kev->udata) == NULL ||
+ c->todestroy)
+ continue;
+
+ if ((kev->flags & EV_EOF) != 0) {
+ client_destroy_mark(c);
+ continue;
+ }
+
+ if (kev->filter == EVFILT_WRITE) {
+ int f;
+
+ /*
+ * If there's a sendq for the client,
+ * attempt to flush it. If there's an error,
+ * drop client.
+ * If client was marked to be closed, and we
+ * finished flushing data, also mark it to be
+ * destroyed, since we're done with it.
+ */
+ if ((f = sendq_flush(&c->sendq, kev->data))
+ == -1 || (f == 0 && c->toclose))
+ client_destroy_mark(c);
+ else if (f == 0) {
+ /*
+ * No more data to send, we can thus
+ * temporarily disable write events
+ * for this descriptor. client_write()
+ * will re-enable it as necessary.
+ */
+ kqueue_sev_alloc(1);
+ kqueue_sev_add(c->fd, EVFILT_WRITE,
+ EV_DISABLE, 0, 0, (intptr_t)c);
+ c->writepolling = 0;
+ }
+ continue;
+ }
+
+ if (kev->filter == EVFILT_READ) {
+ /*
+ * Data to read from client. Simply read
+ * available packets into a 16-bit aligned
+ * queue to process it at the next
+ * server heartbeat event. This also
+ * transparently handles ping requests which
+ * packets are not queued.
+ */
+ if (recvq_read(&c->recvq) == -1) {
+ client_destroy_mark(c);
+ continue;
+ }
+ continue;
+ }
+
+ }
+ /* Destroy marked to be destroyed clients */
+ client_destroy_marked();
+ }
+ /* NOTREACHED */
+}
+
+
+/* Utility functions */
+
+inline void
+kqueue_sev_alloc(int n)
+{
+
+ if (sev_cnt > S_EVENTS - n) {
+ if (kevent(kqid, sev, sev_cnt, NULL, 0, NULL) == -1)
+ syslog(LOG_NOTICE,
+ "kqueue_sev_alloc() - kevent() - %s",
+ strerror(errno));
+ sev_cnt = 0;
+ }
+}
+
+inline void
+kqueue_sev_add(uintptr_t ident, uint32_t filter, uint32_t flags,
+ uint32_t fflags, int64_t data, intptr_t udata)
+{
+
+ /*
+ * Be careful to avoid macro side effects, do not use &sev[sev_cnt++]
+ */
+ EV_SET(&sev[sev_cnt], ident, filter, flags, fflags, data, udata);
+ sev_cnt++;
+}
--- /dev/null
+/* $Id: kqueue.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _KQUEUE_H_
+#define _KQUEUE_H_
+
+
+
+#include <sys/event.h>
+#include <sys/time.h>
+
+
+
+enum kqueue_timers {
+ TIMER_FPS = 0,
+ TIMER_SECOND,
+ TIMER_TIMEOUT,
+ TIMER_MAX
+};
+
+
+
+void kqueue_init(void);
+void kqueue_addlisten(int);
+void kqueue_addsignal(int);
+void kqueue_addtimer(int, int64_t);
+void kqueue_main(void);
+inline void kqueue_sev_alloc(int);
+inline void kqueue_sev_add(uintptr_t, uint32_t, uint32_t, uint32_t,
+ int64_t, intptr_t);
+
+
+
+extern time_t current_time;
+
+
+
+#endif
--- /dev/null
+/* $Id: main.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <mmpool.h>
+#include <mmlist.h>
+
+#include <daemon.h>
+#include <net.h>
+#include <sendq.h>
+#include <trigonometry.h>
+#include <packets_common.h>
+#include <packets.h>
+#include <torp.h>
+#include <client.h>
+#include <enc.h>
+#include <conf.h>
+#include <kqueue.h>
+
+
+
+int main(int, char **);
+
+
+
+int
+main(int argc, char **argv)
+{
+
+ (void) openlog("tms-server", LOG_NDELAY | LOG_PID, LOG_USER);
+
+ if (daemon_init(DAEMON_NODETACH) != 0)
+ exit(EXIT_FAILURE);
+ syslog(LOG_NOTICE, "Started");
+
+ /*
+ * The following funtions exit whenever there's a problem, because
+ * they set up critical things.
+ */
+ client_init();
+ kqueue_init();
+ net_init();
+ trigonometry_init();
+ torp_init();
+ noncerand_init();
+ kqueue_addlisten(listen_fd);
+ kqueue_addsignal(SIGTERM);
+ kqueue_addsignal(SIGPIPE);
+ kqueue_addtimer(TIMER_FPS, FPS_MS);
+ kqueue_addtimer(TIMER_SECOND, 1000);
+ kqueue_addtimer(TIMER_TIMEOUT, TIMEOUT_SECONDS * 1000);
+
+ kqueue_main();
+ /* NOTREACHED */
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/* $Id: net.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <sys/types.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <mmstring.h>
+
+#include <net.h>
+#include <conf.h>
+
+
+
+static int net_listen(const char *, int, int);
+
+
+
+int listen_fd;
+
+
+
+void
+net_init(void)
+{
+
+ if ((listen_fd = net_listen("0.0.0.0", 7777, MAX_CLIENTS)) == -1) {
+ syslog(LOG_NOTICE, "net_listen() - %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int
+net_listen(const char *addr, int port, int backlog)
+{
+ int fd, opt;
+ struct linger linger;
+ struct protoent *pent;
+ struct sockaddr_in server;
+
+ fd = -1;
+
+ mm_memclr(&server, sizeof(struct sockaddr_in));
+ server.sin_family = AF_INET;
+ if (inet_pton(AF_INET, addr, &server.sin_addr) != 1) {
+ syslog(LOG_NOTICE, "inet_pton(%s) - %s", addr,
+ strerror(errno));
+ goto err;
+ }
+ server.sin_port = htons((short)port);
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ syslog(LOG_NOTICE, "socket() - %s", strerror(errno));
+ goto err;
+ }
+
+ opt = 1;
+ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)))
+ == -1)
+ syslog(LOG_NOTICE, "setsockopt(SO_REUSEADDR) - %s",
+ strerror(errno));
+ if ((setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(int)))
+ == -1)
+ syslog(LOG_NOTICE, "setsockopt(SO_KEEPALIVE) - %s",
+ strerror(errno));
+
+ linger.l_onoff = 0;
+ linger.l_linger = 0;
+ if ((setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger,
+ sizeof(struct linger))) == -1)
+ syslog(LOG_NOTICE, "setsockopt(SO_LINGER) - %s",
+ strerror(errno));
+
+ /* XXX Set buffer sizes? */
+
+ if ((pent = getprotobyname("TCP")) != NULL) {
+ opt = 1;
+ if ((setsockopt(fd, pent->p_proto, TCP_NODELAY, &opt,
+ sizeof(int))) == -1)
+ syslog(LOG_NOTICE, "setsockopt(TCP_NODELAY) - %s",
+ strerror(errno));
+ } else
+ syslog(LOG_NOTICE, "getprotobyname(TCP) - %s",
+ strerror(errno));
+
+ if ((opt = fcntl(fd, F_GETFL, NULL)) != -1) {
+ if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) {
+ syslog(LOG_NOTICE, "fcntl(F_SETFL) - %s",
+ strerror(errno));
+ goto err;
+ }
+ } else
+ syslog(LOG_NOTICE, "fcntl(F_GETFL) - %s", strerror(errno));
+
+ if ((bind(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)))
+ != 0) {
+ syslog(LOG_NOTICE, "bind(%s:%d) - %s", addr, port,
+ strerror(errno));
+ goto err;
+ }
+
+ if (listen(fd, backlog) == -1) {
+ syslog(LOG_NOTICE, "listen(%s:%d) - %s", addr, port,
+ strerror(errno));
+ goto err;
+ }
+
+ return fd;
+
+err:
+ if (fd != -1)
+ (void) close(fd);
+
+ return -1;
+}
--- /dev/null
+/* $Id: net.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _NET_H_
+#define _NET_H_
+
+
+
+void net_init(void);
+
+
+
+extern int listen_fd;
+
+
+
+#endif
--- /dev/null
+/* $Id: packets.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * To validate a received packet, we'll make sure that it's larger than
+ * sizeof(int), and that the first integer consists of a valid expected packet
+ * type ID within range. If so, we verify if packet length really corresponds
+ * to the packet type, and then can interpret the packet information.
+ * An actual network packet may contain a number of these packets.
+ * We thus must be able to efficiently determine each packet's length.
+ * Since we're using TCP, it should be enough. However, on the server side
+ * especially, proper sanity checking on input must be done to avoid crashing
+ * the server because of unexpected data processing.
+ */
+
+
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <mmstring.h>
+#include <mmarch.h>
+#include <mmlog.h>
+
+#include <packets_common.h>
+#include <packets.h>
+#include <trigonometry.h>
+#include <client.h>
+#include <enc.h>
+#include <ships.h>
+#include <torp.h>
+#include <sendq.h>
+#include <hmac.h>
+#include <conf.h>
+
+
+
+static int cpacket_auth_handler(client_t *, uint16_t *);
+static int cpacket_ping_handler(client_t *, uint16_t *);
+static int cpacket_pong_handler(client_t *, uint16_t *);
+static int cpacket_direction_handler(client_t *, uint16_t *);
+static int cpacket_thrust_acc_handler(client_t *, uint16_t *);
+static int cpacket_thrust_dec_handler(client_t *, uint16_t *);
+static int cpacket_shield_handler(client_t *, uint16_t *);
+static int cpacket_cloak_handler(client_t *, uint16_t *);
+static int cpacket_torp_handler(client_t *, uint16_t *);
+static int cpacket_slow_handler(client_t *, uint16_t *);
+static int cpacket_fast_handler(client_t *, uint16_t *);
+static int cpacket_quit_handler(client_t *, uint16_t *);
+static int cvpacket_message_handler(client_t *, uint16_t *);
+
+
+
+static int ticks = 0;
+
+
+
+struct packet_index cpacket_index[CVPACKET_MAX] = {
+ /* Fixed sized packets */
+ {sizeof(struct cpacket_auth), cpacket_auth_handler},
+ {sizeof(struct cpacket_ping), cpacket_ping_handler},
+ {sizeof(struct cpacket_pong), cpacket_pong_handler},
+ {sizeof(struct cpacket_direction), cpacket_direction_handler},
+ {sizeof(struct cpacket_thrust_acc), cpacket_thrust_acc_handler},
+ {sizeof(struct cpacket_thrust_dec), cpacket_thrust_dec_handler},
+ {sizeof(struct cpacket_shield), cpacket_shield_handler},
+ {sizeof(struct cpacket_cloak), cpacket_cloak_handler},
+ {sizeof(struct cpacket_torp), cpacket_torp_handler},
+ {sizeof(struct cpacket_slow), cpacket_slow_handler},
+ {sizeof(struct cpacket_fast), cpacket_fast_handler},
+ {sizeof(struct cpacket_quit), cpacket_quit_handler},
+ /* Variable size packets */
+ {sizeof(struct cvpacket_message), cvpacket_message_handler}
+};
+
+
+
+void
+packets_update(void)
+{
+ client_t *c, *next;
+ uint8_t *data;
+ size_t size, off, len;
+
+ /*
+ * XXX We can't do this since if no clients but torps we keep waiting
+ * for torps to all have expired, which never occurs!
+ if (DLIST_NODES(&clients_list) == 0)
+ return;
+ */
+
+ if (++ticks == FPS)
+ ticks = 0;
+
+ for (c = (client_t *)DLIST_TOP(&clients_list); c != NULL; c = next) {
+ next = DLIST_NEXT((node_t *)c);
+
+ if (c->todestroy || c->toclose)
+ continue;
+
+ /* Reset ping request throttling flag */
+ c->askedping = 0;
+
+ /*
+ * First process 16bit-aligned queued incomming packets.
+ * Only full packets are returned by recvq_contents() and
+ * the packets types and sizes have already been validated
+ * by recvq_read().
+ */
+ recvq_content(&c->recvq, &data, &size);
+ for (off = 0; off < size; off += len) {
+ int8_t id;
+
+ /*
+ * Obtain packet type and evaluate its length.
+ * Because they are 16bit-aligned, make sure to
+ * increase len as necessary if it is odd.
+ */
+ id = (int8_t )data[off];
+ if (id >= CPACKET_MAX)
+ len = data[off + 1];
+ else
+ len = cpacket_index[id].size;
+ if ((len & 1) != 0)
+ len++;
+
+ /*
+ * We cannot accept any packets but the authentication
+ * one from unauthenticated clients.
+ */
+ if (!c->authenticated && id != CPACKET_AUTH)
+ goto k;
+
+ /* Kill client on packet processing error */
+ DEBUG_ASSERT(cpacket_index[id].handler != NULL);
+ if (cpacket_index[id].handler(c,
+ (uint16_t *)&data[off]) == -1) {
+ syslog(LOG_NOTICE, "update(%d) - "
+ "Error processing packet 0x%02x",
+ c->fd, (uint8_t)id);
+ goto k;
+ }
+
+ continue;
+
+k:
+ client_destroy_mark(c);
+ break;
+ }
+ recvq_content_reset(&c->recvq);
+
+ /* Run game frame for client ship */
+ if (c->authenticated)
+ ship_update(&c->ship);
+ }
+
+ torps_update();
+
+ /* XXX Send all ships and all torps to all clients for now */
+ DLIST_FOREACH(&clients_list, (node_t *)c) {
+ client_t *c2;
+ torp_t *t;
+
+ if (!c->authenticated)
+ continue;
+
+ /*
+ * Should only be done every second.
+ * Send self information packet.
+ */
+ if (ticks == 0) {
+ (void) spacket_shipinfo_send(c);
+ /* XXX */
+ }
+
+ if (c->slowlevel > 0) {
+ if (c->slowcnt > 0) {
+ c->slowcnt--;
+ continue;
+ } else
+ c->slowcnt = c->slowlevel;
+ }
+
+ DLIST_FOREACH(&clients_list, (node_t *)c2) {
+
+ if (!c2->authenticated)
+ continue;
+
+ if (!c2->ship.cloak_on || c2 == c) {
+ if (spacket_ship_send(c, (uint16_t)c2->fd,
+ &c2->ship) == -1)
+ break;
+ }
+ }
+
+ DLIST_FOREACH(&torps_list, (node_t *)t) {
+ if (spacket_torp_send(c, t) == -1)
+ break;
+ }
+
+ if (spacket_eof_send(c) == -1)
+ break;
+ (void) sendq_zflush(&c->sendq, NULL, NULL);
+ (void) sendq_flush(&c->sendq, -1);
+ }
+}
+
+int
+spacket_auth_send(client_t *c)
+{
+ struct spacket_auth p;
+
+ p.packet_type = SPACKET_AUTH;
+ p.protocol_version = SERVER_VERSION;
+ /*
+ * Generate unique noonce/rand pairs.
+ * We also must remember these because we expect the
+ * cpacket_auth_handler to verify if the reply matches.
+ */
+ noncerand_gen(c->noncerand1, p.noncerand1);
+ noncerand_gen(c->noncerand2, p.noncerand2);
+
+ return client_write(c, (uint8_t *)&p, sizeof(p), 0);
+}
+
+/*
+ * Unlike other functions, sends the packet immediately without buffering.
+ * Also, this function is not called by the general packets input handler,
+ * but by the recvq code.
+ */
+int
+spacket_pong_send(client_t *c)
+{
+ struct spacket_pong p;
+
+ p.packet_type = SPACKET_PONG;
+
+ return sendq_write(&c->sendq, (uint8_t *)&p, sizeof(p), NULL, NULL, 0);
+}
+
+int
+spacket_eof_send(client_t *c)
+{
+ struct spacket_eof p;
+
+ p.packet_type = SPACKET_EOF;
+
+ return client_write(c, (uint8_t *)&p, sizeof(p), 1);
+}
+
+int
+spacket_ship_send(client_t *c, uint16_t id, ship_t *s)
+{
+ struct spacket_ship p;
+
+ p.packet_type = SPACKET_SHIP;
+ p.flags = 0;
+ if (s->shield_on)
+ p.flags |= SHIPF_SHIELD;
+ if (s->cloak_on)
+ p.flags |= SHIPF_CLOAK;
+ p.id = BYTEORDER_NETWORK16(id);
+ p.x = BYTEORDER_NETWORK16(s->x);
+ p.y = BYTEORDER_NETWORK16(s->y);
+ p.angle = (uint8_t)s->angle;
+
+ return client_write(c, (uint8_t *)&p, sizeof(p), 1);
+}
+
+int
+spacket_shipinfo_send(client_t *c)
+{
+ struct spacket_shipinfo p;
+
+ p.packet_type = SPACKET_SHIPINFO;
+ p.thrust = c->ship.thrust;
+ p.fuel = BYTEORDER_NETWORK16(c->ship.fuel);
+ p.shield = BYTEORDER_NETWORK16(c->ship.shield);
+ p.hull = BYTEORDER_NETWORK16(c->ship.hull);
+
+ return client_write(c, (uint8_t *)&p, sizeof(p), 1);
+}
+
+int
+spacket_torp_send(client_t *c, torp_t *t)
+{
+ struct spacket_torp p;
+
+ p.packet_type = SPACKET_TORP;
+ /* p.radius = c->ship.ship->torp_radius; XXX */
+ p.radius = c->ship.ship->torp_radius;
+ p.x = BYTEORDER_NETWORK16(t->x);
+ p.y = BYTEORDER_NETWORK16(t->y);
+
+ return client_write(c, (uint8_t *)&p, sizeof(p), 1);
+}
+
+
+static int
+cpacket_auth_handler(client_t *c, uint16_t *ptr)
+{
+ struct cpacket_auth *p = (struct cpacket_auth *)ptr;
+ uint8_t hmac1[40], hmac2[40], ppasswd[32],
+ phmac1[20], phmac2[20], plogin[16];
+ int validuser;
+#ifdef USE_ENCRYPTION
+ uint8_t key[512];
+#endif
+
+ if (c->authenticated || p->protocol_version != SERVER_VERSION)
+ return -1;
+
+ /*
+ * XXX Only authenticate against a single user/password pair for
+ * now. Should actually check user limits, user existance, etc,
+ * and make sure to take the same time for cryptography for
+ * unexisting users.
+ * Ideally, there also should be a private server key used as
+ * initial tunnel so that the user name be not in pain text.
+ * XXX
+ * Beware to parse login info properly. We might want to not do
+ * string compares but to use whole padding for hash key generation...
+ * XXX
+ * Note that we can't expect this response to be encrypted because we
+ * don't know the user login yet to use a key for... We could if
+ * there was an additional step, and/or of we initially went with
+ * server private key/password tunnel first which we renegotiated
+ * here.
+ */
+
+ /* Pad login and memcmp */
+ /*
+ * XXX We should lookup login in database.
+ */
+ validuser = 0;
+ (void) memset(plogin, '\0', 16);
+ (void) strncpy(plogin, SERVER_LOGIN, 15);
+ if (memcmp(p->login, plogin, 16) == 0)
+ validuser = 1;
+ else
+ syslog(LOG_NOTICE, "cpacket_auth_handler(%d) - Bad user",
+ c->fd);
+
+ /*
+ * Calculate sha1 and rmd160 hmac hashes for password and place first
+ * 80 bits of each into the hmac field. This is calculated using the
+ * nonce+random data previously sent to the client. We do it for both
+ * noncerand.
+ */
+ /* XXX If !validuser we should use a dummy password */
+ (void) memset(ppasswd, '\0', 32);
+ (void) strncpy(ppasswd, SERVER_PASSWD, 31);
+
+ hmac_sha1((uint8_t *)c->noncerand1, 32, ppasswd, 32, hmac1);
+ hmac_rmd160((uint8_t *)c->noncerand1, 32, ppasswd, 32, &hmac1[20]);
+ (void) memcpy(phmac1, hmac1, 10);
+ (void) memcpy(&phmac1[10], &hmac1[20], 10);
+
+ hmac_sha1((uint8_t *)c->noncerand2, 32, ppasswd, 32, hmac2);
+ hmac_rmd160((uint8_t *)c->noncerand2, 32, ppasswd, 32, &hmac2[20]);
+ (void) memcpy(phmac2, hmac2, 10);
+ (void) memcpy(&phmac2[10], &hmac2[20], 10);
+
+ /* Now verify if client results match exactly ours, drop it if not. */
+ if (!validuser || memcmp(phmac1, p->hmac1, 20) != 0 ||
+ memcmp(phmac2, p->hmac2, 20) != 0) {
+ /* XXX Also log username, maybe also hmacs */
+ syslog(LOG_NOTICE,
+ "cpacket_auth_handler(%d) - Failed auth!", c->fd);
+ /* XXX Clear crypto info! We need a common exit point! */
+ return -1;
+ }
+
+#ifdef USE_ENCRYPTION
+ /*
+ * Matching hmacs.
+ * Use the full hmac results (320 bits) to create a session private
+ * key. This is done by using the hmac process again using the
+ * current full hmac against te user password. This generates a 320
+ * bits key for use with the mmenc cipher, which is immediately
+ * enabled. This is done to create an input key wiht hmac1 and output
+ * one with hmac2.
+ */
+ hmac_sha1(hmac1, 40, ppasswd, 32, key);
+ hmac_rmd160(hmac1, 40, ppasswd, 32, &key[20]);
+ mmenc_init(&c->sendq.enc_out, key, 40);
+
+ hmac_sha1(hmac2, 40, ppasswd, 32, key);
+ hmac_rmd160(hmac2, 40, ppasswd, 32, &key[20]);
+ mmenc_init(&c->recvq.enc_in, key, 40);
+
+ c->mmencrypt = 1;
+
+ (void) memset(key, '\0', 40);
+#endif
+
+ (void) memset(hmac1, '\0', 40);
+ (void) memset(hmac2, '\0', 40);
+ (void) memset(phmac1, '\0', 40);
+ (void) memset(phmac2, '\0', 40);
+ (void) memset(ppasswd, '\0', 32);
+
+ c->authenticated = 1;
+
+ return 0;
+}
+
+/*
+ * Note: Following function is never used, since pings are handled in the
+ * kqueue main loop code.
+ */
+/* ARGSUSED */
+static int
+cpacket_ping_handler(client_t *c, uint16_t *ptr)
+{
+ /* NOOP */
+
+ return 0;
+}
+
+static int
+cpacket_pong_handler(client_t *c, uint16_t *ptr)
+{
+/* struct cpacket_pong *p = (struct cpacket_pong *)ptr;*/
+
+ /* XXX NOOP */
+
+ return 0;
+}
+
+static int
+cpacket_direction_handler(client_t *c, uint16_t *ptr)
+{
+ struct cpacket_direction *p = (struct cpacket_direction *)ptr;
+
+ /* uint8_t so is always between 0 and 255 */
+ c->ship.i_angle = p->angle;
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_thrust_acc_handler(client_t *c, uint16_t *ptr)
+{
+
+ if (++(c->ship.i_thrust) > c->ship.ship->thrust_max)
+ c->ship.i_thrust = c->ship.ship->thrust_max;
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_thrust_dec_handler(client_t *c, uint16_t *ptr)
+{
+
+ if (--(c->ship.i_thrust) < 0)
+ c->ship.i_thrust = 0;
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_shield_handler(client_t *c, uint16_t *ptr)
+{
+
+ c->ship.i_shield_on = (c->ship.i_shield_on != 0 ? 0 : 1);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_cloak_handler(client_t *c, uint16_t *ptr)
+{
+
+ c->ship.i_cloak_on = (c->ship.i_cloak_on != 0 ? 0 : 1);
+
+ return 0;
+}
+
+static int
+cpacket_torp_handler(client_t *c, uint16_t *ptr)
+{
+ struct cpacket_torp *p = (struct cpacket_torp *)ptr;
+
+ torp_create(c, p->angle);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_slow_handler(client_t *c, uint16_t *cptr)
+{
+
+ if (c->slowlevel < 2)
+ c->slowlevel++;
+ c->slowcnt = c->slowlevel;
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_fast_handler(client_t *c, uint16_t *cptr)
+{
+
+ if (c->slowlevel > 0)
+ c->slowlevel--;
+ c->slowcnt = c->slowlevel;
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+cpacket_quit_handler(client_t *c, uint16_t *ptr)
+{
+
+ return -1;
+}
+
+
+/* ARGSUSED */
+static int
+cvpacket_message_handler(client_t *c, uint16_t *ptr)
+{
+/* struct cvpacket_message *p = (struct cvpacket_message *)ptr;*/
+
+ /* XXX NOOP */
+ return 0;
+}
--- /dev/null
+/* $Id: packets.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _PACKETS_H_
+#define _PACKETS_H_
+
+
+
+#include <client.h>
+#include <packets_common.h>
+#include <ships.h>
+#include <torp.h>
+
+
+
+/*
+ * For every server packet type we support, an index array will be used
+ * with this structure to call the associated handler function, which will
+ * perform sanity checking and process the packet, returning 0 on success or
+ * -1 on error. The size field will allow to perform sanity checking on
+ * data size prior to calling the handler function, as well as to increase the
+ * buffer pointer.
+ */
+struct packet_index {
+ size_t size;
+ int (*handler)(client_t *, uint16_t *);
+};
+
+
+
+void packets_init(void);
+void packets_update(void);
+int spacket_auth_send(client_t *);
+int spacket_pong_send(client_t *);
+int spacket_eof_send(client_t *);
+int spacket_ship_send(client_t *, uint16_t, ship_t *);
+int spacket_shipinfo_send(client_t *);
+int spacket_torp_send(client_t *, torp_t *);
+
+
+
+extern struct packet_index cpacket_index[CVPACKET_MAX];
+
+
+
+#endif
--- /dev/null
+/* $Id: recvq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <errno.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <mmstring.h>
+
+#include <recvq.h>
+#include <conf.h>
+
+#ifdef USE_ENCRYPTION
+#include <mmenc.h>
+#endif
+
+#include <packets.h>
+#include <client.h>
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+
+
+#define RBUFFER_SIZE 4096
+#ifdef USE_COMPRESSION
+#define IBUFFER_SIZE (RBUFFER_SIZE * 64)
+#endif
+
+
+
+static int8_t rbuffer[RBUFFER_SIZE];
+#ifdef USE_COMPRESSION
+static char ibuffer[IBUFFER_SIZE];
+#endif
+
+
+
+int
+recvq_init(recvq_t *q, size_t size)
+{
+
+ if ((size & 1) != 0)
+ size++;
+
+ if ((q->buffer = malloc(size)) != NULL) {
+#ifdef USE_COMPRESSION
+ q->zin.next_in = q->zin.next_out = NULL;
+ q->zin.avail_in = q->zin.avail_out = 0;
+ q->zin.zalloc = NULL;
+ q->zin.zfree = NULL;
+ q->zin.opaque = NULL;
+ if (inflateInit(&q->zin) == Z_OK) {
+ q->size = size;
+
+ return 0;
+ } else {
+ syslog(LOG_NOTICE, "recvq_init() - inflateInit() - %s",
+ strerror(errno));
+ free(q->buffer);
+ }
+#else
+ q->size = size;
+ return 0;
+#endif /* USE_COMPRESSION */
+ } else
+ syslog(LOG_NOTICE, "recvq_init() - malloc() - %s",
+ strerror(errno));
+
+ return -1;
+}
+
+void
+recvq_destroy(recvq_t *q)
+{
+
+ free(q->buffer);
+#ifdef USE_COMPRESSION
+ (void) inflateEnd(&q->zin);
+#endif
+#ifdef USE_ENCRYPTION
+ (void) memset(&q->enc_in, '\0', sizeof(mmenc_t));
+#endif
+}
+
+void
+recvq_reset(recvq_t *q, void *cptr)
+{
+ client_t *c = (client_t *)cptr;
+
+ q->client = c;
+ q->fd = c->fd;
+ q->tail = q->ptail = 0;
+
+ q->variable = q->last = -1;
+ q->length = -1;
+
+ q->recvpackets = 0;
+
+#ifdef USE_COMPRESSION
+ (void) inflateReset(&q->zin);
+#endif
+}
+
+/*
+ * Reads up to RBUFFER_SIZE of ready data from the socket and fills the
+ * received packets queue buffer. Buffered packets are 16-bit aligned with 0
+ * padding. Minimizes calls to read(2) by using a buffer and recopying it
+ * instead of reading one byte at a time for the packet type and length.
+ * Automatically proceses CPACKET_PING for accurate latency evaluation,
+ * which packets will not be queued.
+ * Returns 0 on success or -1 on error.
+ */
+int
+recvq_read(recvq_t *q)
+{
+ ssize_t s;
+ int8_t *ptr, *tptr, *buf;
+ client_t *c = (client_t *)q->client;
+
+ /*
+ * First read available data in an unaligned buffer, which may then
+ * begin and end by partial packets and contain multiple unaligned
+ * packets.
+ */
+ {
+ int eof = 0;
+#ifdef USE_COMPRESSION
+ int ret;
+#endif
+
+ if ((s = read(q->fd, rbuffer, RBUFFER_SIZE)) == -1) {
+ if (errno == EAGAIN)
+ return 0;
+ else
+ eof = 1;
+ }
+ if (s == 0)
+ eof = 1;
+
+ if (eof) {
+ syslog(LOG_NOTICE, "recvq_read(%d) - EOF", q->fd);
+ return -1;
+ }
+
+#ifdef USE_ENCRYPTION
+ /* Decrypt incomming data */
+ if (c->mmencrypt)
+ mmenc_decrypt(&q->enc_in, rbuffer, s);
+#endif
+
+#ifdef USE_COMPRESSION
+ /* Decompress incomming data */
+ q->zin.next_in = rbuffer;
+ q->zin.avail_in = s;
+ q->zin.next_out = ibuffer;
+ q->zin.avail_out = IBUFFER_SIZE;
+ if ((ret = inflate(&q->zin, Z_SYNC_FLUSH)) != Z_OK &&
+ ret != Z_STREAM_END) {
+ syslog(LOG_NOTICE, "recvq_read(%d) - inflate()",
+ q->fd);
+ return -1;
+ }
+ buf = ibuffer;
+ s = IBUFFER_SIZE - q->zin.avail_out;
+#else
+ buf = rbuffer;
+#endif /* USE_COMPRESSION */
+ }
+
+ /*
+ * Process previous raw buffer and queue aligned full packets.
+ * We take care to verify recvq boundaries when adding even bytes,
+ * we know that the buffer is 16-bit aligned.
+ */
+ for (ptr = buf, tptr = &buf[s]; ptr < tptr; ) {
+ if (q->length != -1) {
+ /* Still reading incomplete packet */
+
+ if (q->length > 0) {
+ /* Byte may be odd or even check recvq limit */
+ if (q->tail == q->size) {
+ syslog(LOG_NOTICE,
+ "recv_read(%d) - recvq full",
+ q->fd);
+ return -1;
+ }
+ q->buffer[q->tail++] = *ptr++;
+ q->length--;
+ }
+ if (q->length == 0) {
+complete:
+ /* Packet complete, align buffer for next */
+ q->length = -1;
+ if ((q->tail & 1) != 0)
+ q->buffer[q->tail++] = 0;
+
+ /*
+ * Update counter for timeout checking
+ * we don't account received ping or fast
+ * packets, because they are automatically
+ * sent by the client regularily.
+ */
+ if (q->last != CPACKET_PING &&
+ q->last != CPACKET_FAST)
+ q->recvpackets++;
+
+ /*
+ * Immediately process ping request packets
+ * since we must properly permit to measure
+ * the network latency. We also dequeue ping
+ * packets transparently.
+ * We only allow one ping request from server
+ * heartbeat cycle, and we only allow them if
+ * the client is already authenticated.
+ * Note that server pong packets are sent
+ * directly rather than passing through the
+ * sendq, unlike other packets.
+ */
+ if (q->last == CPACKET_PING) {
+ /* Discard packet from queue */
+ q->tail -= 2;
+ if (!c->authenticated ||
+ c->askedping ||
+ spacket_pong_send(c) == -1) {
+ syslog(LOG_NOTICE,
+ "recvq_read(%d) - "
+ "spacket_pong_send()",
+ q->fd);
+ return -1;
+ }
+ c->askedping = 1;
+ continue;
+ }
+
+ /*
+ * Update the completed packets tail pointer,
+ * ptail.
+ */
+ q->ptail = q->tail;
+
+ continue;
+ }
+ } else {
+ /*
+ * New packet. We must evaluate packet length by
+ * packet type, and take into consideration variable
+ * packets which length will be provided as a second
+ * byte. For variable packets, we must make sure that
+ * the received length is at least high enough for the
+ * minimum length of the packet.
+ */
+ if (q->variable != -1) {
+ /* Receiving length of variable packet */
+ q->length = (ssize_t)((uint8_t)*ptr);
+ if (q->length <
+ cpacket_index[q->variable].size) {
+ syslog(LOG_NOTICE,
+ "recvq_read(%d) - "
+ "Invalid packet size %d",
+ q->fd, q->length);
+ q->variable = -1;
+ q->length = -1;
+ return -1;
+ }
+ q->variable = -1;
+
+ /* Odd */
+ q->buffer[q->tail++] = *ptr++;
+ q->length--;
+ } else {
+ int8_t t;
+
+ /* Receiving type of packet. Even */
+ if (q->tail == q->size) {
+ syslog(LOG_NOTICE,
+ "recv_read(%d) - recvq full",
+ q->fd);
+ return -1;
+ }
+ t = (q->buffer[q->tail++] = *ptr++);
+ q->last = t;
+
+ if (t < 0 || t > CVPACKET_MAX) {
+ syslog(LOG_NOTICE, "recv_read(%d) - "
+ "Invalid packet type 0x%02x",
+ q->fd, (uint8_t)t);
+ return -1;
+ }
+ if (t < CPACKET_MAX) {
+ /* Fixed packet */
+ q->length = cpacket_index[t].size - 1;
+ q->variable = -1;
+ /*
+ * Special provision for single byte
+ * packets
+ */
+ if (q->length == 0)
+ goto complete;
+ } else {
+ /* Variable packet, awaiting size */
+ q->length = -1;
+ q->variable = t;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Returns a pointer to the starting point of the buffer and the size of the
+ * buffer. May contain multiple contiguous 16-bit aligned packets with
+ * padding. Only full packets are returned.
+ */
+void
+recvq_content(recvq_t *q, uint8_t **buf, size_t *size)
+{
+
+ *buf = (uint8_t *)q->buffer;
+ *size = q->ptail;
+ if ((*size & 1) != 0)
+ (*size)++;
+}
+
+/*
+ * To be called after recvq_content() supplied buffer is processed.
+ * Repositions offsets while making sure to preserve any previously incomplete
+ * packets.
+ */
+void
+recvq_content_reset(recvq_t *q)
+{
+ if (q->ptail == 0)
+ return;
+
+ if (q->tail > q->ptail) {
+ register size_t s;
+
+ s = q->tail - q->ptail;
+ (void) mm_memcpy(q->buffer, &q->buffer[q->ptail], s);
+ q->tail = s;
+ } else
+ q->tail = 0;
+
+ q->ptail = 0;
+}
--- /dev/null
+/* $Id: recvq.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _RECVQ_H_
+#define _RECVQ_H_
+
+
+
+#include <sys/types.h>
+
+#include <conf.h>
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+#ifdef USE_ENCRYPTION
+#include <mmenc.h>
+#include <enc.h>
+#endif
+
+
+
+typedef struct recvq {
+ void *client; /* (client_t *) */
+ int fd;
+ uint8_t *buffer;
+ size_t size, tail, ptail;
+ /* Necessary for fragmented packets reassembly */
+ int variable, last;
+ ssize_t length;
+ /* To implement input timeout */
+ int recvpackets;
+#ifdef USE_COMPRESSION
+ z_stream zin;
+#endif
+#ifdef USE_ENCRYPTION
+ mmenc_t enc_in;
+#endif
+} recvq_t;
+
+
+
+int recvq_init(recvq_t *, size_t);
+void recvq_destroy(recvq_t *);
+void recvq_reset(recvq_t *, void *);
+int recvq_read(recvq_t *);
+void recvq_content(recvq_t *, uint8_t **, size_t *);
+void recvq_content_reset(recvq_t *);
+
+
+
+#endif
--- /dev/null
+/* $Id: sendq.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <mmlist.h>
+#include <mmstring.h>
+
+#include <sendq.h>
+#include <client.h>
+#include <conf.h>
+
+#ifdef USE_ENCRYPTION
+#include <mmenc.h>
+#endif
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+
+
+#ifdef USE_COMPRESSION
+#define DBUFFER_SIZE 16384
+#endif
+
+
+#ifdef USE_COMPRESSION
+static char dbuffer[DBUFFER_SIZE];
+#endif
+
+
+
+/*
+ * Initializes a sendq object, allocating the queue buffer
+ */
+int
+sendq_init(sendq_t *q, size_t size)
+{
+
+ if ((q->buffer = malloc(size)) != NULL) {
+#ifdef USE_COMPRESSION
+ q->zout.next_in = q->zout.next_out = NULL;
+ q->zout.avail_in = q->zout.avail_out = 0;
+ q->zout.zalloc = NULL;
+ q->zout.zfree = NULL;
+ q->zout.opaque = NULL;
+ if (deflateInit(&q->zout, Z_DEFAULT_COMPRESSION) == Z_OK) {
+ q->size = size;
+
+ return 0;
+ } else {
+ syslog(LOG_NOTICE, "sendq_init() - deflateInit() - %s",
+ strerror(errno));
+ free(q->buffer);
+ }
+#else
+ q->size = size;
+ return 0;
+#endif /* USE_COMPRESSION */
+ } else
+ syslog(LOG_NOTICE, "sendq_init() - malloc() - %s",
+ strerror(errno));
+
+ return -1;
+}
+
+void
+sendq_reset(sendq_t *q, void *cptr)
+{
+ client_t *c = (client_t *)cptr;
+
+ q->client = c;
+ q->fd = c->fd;
+ q->head = q->tail = 0;
+
+#ifdef USE_COMPRESSION
+ (void) deflateReset(&q->zout);
+#endif
+}
+
+/*
+ * Destroys a sendq object, freeing the queue buffer.
+ * Note that this only gets called when a full page of client objects are
+ * destroyed. As such, they cryptographic keys, if any, are destroyed by
+ * client_destroy_marked().
+ */
+void
+sendq_destroy(sendq_t *q)
+{
+
+ free(q->buffer);
+#ifdef USE_COMPRESSION
+ (void) deflateEnd(&q->zout);
+#endif
+}
+
+/*
+ * Attempts to write to the non-blocking socket. In the case of a partial
+ * write, queue the remaining of the buffer. In case where the buffer is
+ * full, return an error (-1), in which case the sendq has been exceeded and
+ * client should be dropped. We only write(2) if there is no pending buffers
+ * already, of course. If bfunc is not NULL, it will be called with passed
+ * argument bfuncarg whenever data that couldn't be written immediately is
+ * buffered. If buffer is true, no attempt will be made at a real write; Data
+ * will simply be buffered if possible.
+ *
+ * XXX There is a potential problem where if the client is lagged enough to
+ * have filled the buffer, although not enough to exceed the queue but that
+ * the client cannot recoup, we could consider the queue filled and drop the
+ * client. This because we aren't really using a real FIFO buffer. This code
+ * should probably use the mmfifo(3) library after the multiple bytes buffer
+ * functions in it have been debugged and tested properly.
+ * However, since if the user was this behind this would in practice mean that
+ * his commands would take a while to be reflected, this is probably okay for
+ * the time being.
+ */
+int
+sendq_write(sendq_t *q, uint8_t *buf, size_t size,
+ void (*bfunc)(void *), void *bfuncarg, int buffer)
+{
+#ifdef USE_ENCRYPTION
+ client_t *c = (client_t *)q->client;
+#endif
+
+ /* Compress outgoing data, in flushed mode if unbuffered */
+#ifdef USE_COMPRESSION
+ q->zout.next_in = buf;
+ q->zout.avail_in = size;
+ q->zout.next_out = dbuffer;
+ q->zout.avail_out = DBUFFER_SIZE;
+ if (deflate(&q->zout, (buffer ? Z_NO_FLUSH : Z_SYNC_FLUSH)) != Z_OK)
+ return -1;
+ buf = dbuffer;
+ size = DBUFFER_SIZE - q->zout.avail_out;
+#endif
+
+ /* Queue new data to sendq buffer */
+ if (size > 0) {
+#ifdef USE_ENCRYPTION
+ /* Encrypt outgoing data */
+ if (c->mmencrypt)
+ mmenc_encrypt(&q->enc_out, buf, size);
+#endif
+
+ if (q->tail + size > q->size)
+ return -1;
+
+ (void) mm_memcpy(&q->buffer[q->tail], buf, size);
+ q->tail += size;
+ if (bfunc != NULL && buffer)
+ bfunc(bfuncarg);
+ }
+
+ /* Flush sendq if unbuffered write request */
+ if (size > 0 && !buffer)
+ return sendq_flush(q, -1);
+
+ return 0;
+}
+
+/*
+ * If any pending buffers exist, attempts to write them. In the case of an
+ * error, returns -1, in which case the client should be dropped. If there
+ * still remains unflushed data, 1 is returned. Otherwise, 0 is.
+ */
+int
+sendq_flush(sendq_t *q, size_t room)
+{
+
+ if (q->head != q->tail) {
+ ssize_t ns;
+ size_t os = q->tail - q->head;
+
+ /* Only attempt write(2) syscall if there is room */
+ if (room == 0)
+ return -1;
+
+ if ((ns = write(q->fd, &q->buffer[q->head], os)) == -1) {
+ if (errno == EAGAIN)
+ return 1;
+ else
+ return -1;
+ }
+
+ if ((q->head += ns) == q->tail)
+ q->head = q->tail = 0;
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Allows to flush the zlib compression output buffer.
+ * Queues any flushed data to the sendq.
+ */
+int
+sendq_zflush(sendq_t *q, void (*bfunc)(void *), void *bfuncarg)
+{
+#ifdef USE_ENCRYPTION
+ client_t *c = (client_t *)q->client;
+#endif
+
+#ifdef USE_COMPRESSION
+ char ch;
+ size_t size;
+
+ q->zout.next_in = &ch;
+ q->zout.avail_in = 0;
+ q->zout.next_out = dbuffer;
+ q->zout.avail_out = DBUFFER_SIZE;
+ if (deflate(&q->zout, Z_SYNC_FLUSH) != Z_OK)
+ return -1;
+
+ if ((size = DBUFFER_SIZE - q->zout.avail_out) > 0) {
+ if (q->tail + size > q->size)
+ return -1;
+
+#ifdef USE_ENCRYPTION
+ /* Encrypt outgoing data */
+ if (c->mmencrypt)
+ mmenc_encrypt(&q->enc_out, dbuffer, size);
+#endif
+
+ (void) mm_memcpy(&q->buffer[q->tail], dbuffer, size);
+ q->tail += size;
+ if (bfunc != NULL)
+ bfunc(bfuncarg);
+ }
+#endif
+
+ return 0;
+}
--- /dev/null
+/* $Id: sendq.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _SENDQ_H_
+#define _SENDQ_H_
+
+
+
+#include <sys/types.h>
+
+#include <conf.h>
+
+#ifdef USE_COMPRESSION
+#include <zlib.h>
+#endif
+
+#include <mmlist.h>
+
+#ifdef USE_ENCRYPTION
+#include <mmenc.h>
+#include <enc.h>
+#endif
+
+
+
+typedef struct sendq {
+ void *client; /* (client_t *) */
+ int fd;
+ uint8_t *buffer;
+ size_t size, head, tail;
+#ifdef USE_COMPRESSION
+ z_stream zout;
+#endif
+#ifdef USE_ENCRYPTION
+ mmenc_t enc_out;
+#endif
+} sendq_t;
+
+
+
+int sendq_init(sendq_t *, size_t);
+void sendq_destroy(sendq_t *);
+void sendq_reset(sendq_t *, void *);
+int sendq_write(sendq_t *, uint8_t *, size_t, void (*)(void *), void *,
+ int);
+int sendq_zflush(sendq_t *, void (*)(void *), void *);
+int sendq_flush(sendq_t *, size_t);
+
+
+
+#endif
--- /dev/null
+/* $Id: ships.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <mmlog.h>
+
+#include <conf.h>
+#include <ships.h>
+#include <trigonometry.h>
+
+
+
+/* XXX We need to set these to decent values */
+struct shipdesc ships[SHIP_MAX] = {
+ { /* CA */
+ 30.0, /* Radius */
+ 10.0, /* Weight */
+ 100.0, /* Hull max */
+ 1.0, /* Hull regen */
+ 100.0, /* Shield max */
+ 10.0, /* Shield cost */
+ 3.0, /* Shield regen */
+ 50.0, /* Cloak cost */
+ 10000.0, /* Fuel max */
+ 30.0, /* Fuel regen */
+ 9.0, /* Thrust max */
+ 0.20, /* Thrust acc */
+ 0.30, /* Thrust dec */
+ 5.0, /* Thrust acc cost */
+ 5.0, /* Thrust dec cost */
+ 6.0, /* Thrust cost */
+ 80/*2.0*/, /* Turn thrust */
+ 0.5, /* Turn cost */
+ 100.0, /* Torp cost */
+ 90.0, /* Torp damage */
+ 2.0, /* Torp radius */
+ 11.0, /* Torp speed */
+ 60, /* Torp life */
+ 90.0, /* Phaser cost */
+ 90.0, /* Phaser damage */
+ 5.0, /* Phaser recharge */
+ 100.0 /* Phaser radius */
+ },
+ { /* DD */
+ 40.0, /* Radius */
+ 10.0, /* Weight */
+ 100.0, /* Hull max */
+ 1.0, /* Hull regen */
+ 100.0, /* Shield max */
+ 10.0, /* Shield cost */
+ 3.0, /* Shield regen */
+ 50.0, /* Cloak cost */
+ 10000.0, /* Fuel max */
+ 50.0, /* Fuel regen */
+ 9.0, /* Thrust max */
+ 0.60, /* Thrust acc */
+ 0.60, /* Thrust dec */
+ 2.0, /* Thrust acc cost */
+ 2.0, /* Thrust dec cost */
+ 1.0, /* Thrust cost */
+ 1.0, /* Turn thrust */
+ 0.5, /* Turn cost */
+ 90.0, /* Torp cost */
+ 90.0, /* Torp damage */
+ 3.0, /* Torp radius */
+ 12.0, /* Torp speed */
+ 60, /* Torp life */
+ 90.0, /* Phaser cost */
+ 90.0, /* Phaser damage */
+ 5.0, /* Phaser recharge */
+ 100.0 /* Phaser radius */
+ }
+};
+
+
+
+void
+ship_init(ship_t *s, int type)
+{
+ struct shipdesc *d;
+
+ DEBUG_ASSERT(s != NULL);
+ DEBUG_ASSERT(type > -1 && type < SHIP_MAX);
+
+ d = &ships[type];
+
+ s->ship = d;
+ s->hull = d->hull_max;
+ s->shield = d->shield_max;
+ s->fuel = d->fuel_max;
+ s->thrust = s->i_thrust = 0.0;
+ s->phaser_delay = 0.0;
+ s->shield_on = s->i_shield_on = 1;
+ s->cloak_on = s->i_cloak_on = 0;
+ s->torps = 0;
+
+ /* XXX Random position/angle for now */
+ s->angle = s->i_angle = (random() % 255);
+ /*
+ s->x = d->radius + (random() % (WORLD_X_MAX - (d->radius * 2) - 1));
+ s->y = d->radius + (random() % (WORLD_Y_MAX - (d->radius * 2) - 1));
+ XXX
+ */
+ s->x = WORLD_X_MAX / 2;
+ s->y = WORLD_Y_MAX / 2;
+}
+
+void
+ship_update(ship_t *s)
+{
+ double d;
+
+ /*
+ * Regenerate resources
+ */
+ if (s->fuel < s->ship->fuel_max) {
+ if ((s->fuel += s->ship->fuel_regen) > s->ship->fuel_max)
+ s->fuel = s->ship->fuel_max;
+ }
+ if (s->shield < s->ship->shield_max) {
+ if ((s->shield += s->ship->shield_regen) > s->ship->shield_max)
+ s->shield = s->ship->shield_max;
+ }
+ if (s->hull < s->ship->hull_max) {
+ if ((s->hull += s->ship->hull_regen) > s->ship->hull_max)
+ s->hull = s->ship->hull_max;
+ }
+ if (s->phaser_delay > 0) {
+ if ((s->phaser_delay -= s->ship->phaser_recharge) < 0)
+ s->phaser_delay = 0;
+ }
+
+ /*
+ * Adjust angle towards intended direction and account cost
+ */
+ if (s->fuel >= s->ship->turn_cost && s->angle != s->i_angle) {
+ s->fuel -= s->ship->turn_cost;
+ int i, min, max;
+
+ /*
+ * Calculate how much we can turn taking into account
+ * inertia relative to thrust/velocity
+ */
+ /*
+ i = (int)(s->ship->turn_thrust *
+ (s->ship->thrust_max - s->thrust + 0.3));
+ */
+
+ if (s->thrust == 0)
+ i = 255;
+ else
+ i = (int)(s->ship->turn_thrust /
+ (s->thrust * s->thrust));
+
+ /*
+ * Turn in wanted direction if needed, taking into account
+ * the shortest path
+ */
+ if (s->i_angle < s->angle) {
+ min = s->i_angle;
+ max = s->angle;
+ } else {
+ min = s->angle;
+ max = s->i_angle;
+ }
+ if (i > max - min)
+ i = max - min;
+
+ if (i > 256 - max + min)
+ s->angle = s->i_angle;
+ else if ((uint8_t)(s->angle - s->i_angle) > 127)
+ s->angle += i;
+ else
+ s->angle -= i;
+
+ s->angle &= 0xff;
+ }
+
+ /*
+ * Adjust thrust towards intended thrust if needed and account
+ * acceleration/deceleration cost
+ */
+ if (s->fuel >= s->ship->thrust_acc_cost && s->thrust < s->i_thrust) {
+ s->fuel -= s->ship->thrust_acc_cost;
+ if ((s->thrust += s->ship->thrust_acc) > s->i_thrust)
+ s->thrust = s->i_thrust;
+ } else if (s->fuel >= s->ship->thrust_dec_cost &&
+ s->thrust > s->i_thrust) {
+ s->fuel -= s->ship->thrust_dec_cost;
+ if ((s->thrust -= s->ship->thrust_dec) < s->i_thrust)
+ s->thrust = s->i_thrust;
+ }
+
+ /*
+ * Adjust ship position and account thrust cost.
+ * If we don't have enough fuel to maintain current thrust, we
+ * decelerate at no cost.
+ */
+ if (s->thrust > 0 &&
+ s->fuel >= (d = s->ship->thrust_cost * s->thrust)) {
+ int i, ox, oy, r;
+
+ r = s->ship->radius;
+ ox = s->x;
+ oy = s->y;
+ s->fuel -= d;
+ i = ((int)s->thrust) + 1;
+ s->x = VECTOR_X(s->x, s->angle, i);
+ s->y = VECTOR_Y(s->y, s->angle, i);
+ /* XXX Observe world limits and block/bounce */
+ if (s->x < r || s->x > WORLD_X_MAX - r - 1 ||
+ s->y < r || s->y > WORLD_Y_MAX - r - 1) {
+ s->x = ox;
+ s->y = oy;
+ s->angle = s->i_angle = angle_bounce(s->angle);
+ }
+ } else {
+ if ((s->thrust -= s->ship->thrust_dec) < 0)
+ s->thrust = 0;
+ s->i_thrust = s->thrust;
+ }
+
+ /*
+ * Adjust shield and cloak states. Movement is prioritary.
+ */
+ if (s->i_shield_on && s->fuel >= s->ship->shield_cost) {
+ s->fuel -= s->ship->shield_cost;
+ s->shield_on = 1;
+ } else
+ s->shield_on = 0;
+ if (s->i_cloak_on && s->fuel >= s->ship->cloak_cost) {
+ s->fuel -= s->ship->cloak_cost;
+ s->cloak_on = 1;
+ } else
+ s->cloak_on = 0;
+}
--- /dev/null
+/* $Id: ships.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _SHIPS_H_
+#define _SHIPS_H_
+
+
+
+enum ship_types {
+ SHIP_CA = 0,
+ SHIP_DD,
+ SHIP_MAX
+};
+
+struct shipdesc {
+ double radius;
+ double weight;
+ double hull_max, hull_regen;
+ double shield_max, shield_cost, shield_regen;
+ double cloak_cost;
+ double fuel_max, fuel_regen;
+ double thrust_max, thrust_acc, thrust_dec,
+ thrust_acc_cost, thrust_dec_cost, thrust_cost;
+ double turn_thrust, turn_cost;
+ double torp_cost, torp_damage, torp_radius, torp_speed;
+ int torp_life;
+ double phaser_cost, phaser_damage, phaser_recharge,
+ phaser_radius;
+};
+
+typedef struct ship {
+ struct shipdesc *ship;
+ double hull;
+ double shield;
+ double fuel;
+ double thrust, i_thrust;
+ double phaser_delay; /* phaser_cost - phaser_recharge */
+ int shield_on, i_shield_on;
+ int cloak_on, i_cloak_on;
+ int torps;
+ int angle, i_angle;
+ int x, y;
+} ship_t;
+
+
+
+void ship_init(ship_t *, int);
+void ship_update(ship_t *);
+
+
+
+extern struct shipdesc ships[SHIP_MAX];
+
+
+
+#endif
--- /dev/null
+/* $Id: torp.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <mmlog.h>
+
+#include <torp.h>
+#include <trigonometry.h>
+#include <conf.h>
+
+
+
+list_t torps_list;
+static pool_t torps_pool;
+
+
+
+void
+torp_init(void)
+{
+
+ DLIST_INIT(&torps_list);
+ if (!pool_init(&torps_pool, "torps_pool", malloc, free, NULL, NULL,
+ sizeof(torp_t), 65536 / sizeof(torp_t), 1, 0)) {
+ syslog(LOG_NOTICE, "torp_init() - pool_init()");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void
+torp_create(client_t *c, int angle)
+{
+ torp_t *t;
+
+ if (c->ship.torps > 15)
+ return;
+
+ if (c->ship.fuel < c->ship.ship->torp_cost)
+ return;
+ c->ship.fuel -= c->ship.ship->torp_cost;
+
+ if ((t = (torp_t *)pool_alloc(&torps_pool, FALSE)) != NULL) {
+ int r = c->ship.ship->torp_radius;
+
+ t->client = c;
+ t->life = t->client->ship.ship->torp_life;
+ t->angle = angle;
+ t->x = VECTOR_X(c->ship.x, angle, c->ship.ship->radius);
+ t->y = VECTOR_Y(c->ship.y, angle, c->ship.ship->radius);
+ if (t->x < r)
+ t->x = r;
+ if (t->y < r)
+ t->y = r;
+ if (t->x > WORLD_X_MAX - 1 - r)
+ t->x = WORLD_X_MAX - 1 - r;
+ if (t->y > WORLD_Y_MAX - 1 - r)
+ t->x = WORLD_Y_MAX - 1 - r;
+ c->ship.torps++;
+ DLIST_APPEND(&torps_list, (node_t *)t);
+ } else
+ syslog(LOG_NOTICE, "torp_create() - pool_alloc(torps_list)");
+}
+
+/*
+ * XXX We probably should cause an explosion here, possibly type specified
+ * as argument.
+ */
+void
+torp_destroy(torp_t *t)
+{
+
+ DEBUG_ASSERT(t != NULL);
+
+ t->client->ship.torps--;
+ DLIST_UNLINK(&torps_list, (node_t *)t);
+ (void) pool_free((pnode_t *)t);
+}
+
+void
+torps_update(void)
+{
+ torp_t *t, *next;
+ int s, r, ox, oy;
+
+ for (t = (torp_t *)DLIST_TOP(&torps_list); t != NULL; t = next) {
+ next = DLIST_NEXT((node_t *)t);
+
+ if (--(t->life) == 0) {
+ torp_destroy(t);
+ continue;
+ }
+
+ ox = t->x;
+ oy = t->y;
+ s = t->client->ship.ship->torp_speed;
+ r = t->client->ship.ship->torp_radius;
+ t->x = VECTOR_X(t->x, t->angle, s);
+ t->y = VECTOR_Y(t->y, t->angle, s);
+ t->x += (-4 + (random() % 8));
+ t->y += (-4 + (random() % 8));
+ if (t->x < r || t->x > WORLD_X_MAX - 1 - r ||
+ t->y < r || t->y > WORLD_Y_MAX - 1 - r) {
+ t->x = ox;
+ t->y = oy;
+ t->angle = angle_bounce(t->angle);
+ }
+ }
+}
--- /dev/null
+/* $Id: torp.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _TORP_H_
+#define _TORP_H_
+
+
+
+#include <client.h>
+
+
+
+/*
+ * Through the client_t structure we access the ship_t as well and can thus
+ * determine parameters like torp_damage, torp_radius etc.
+ */
+typedef struct torp {
+ pnode_t pnode;
+ client_t *client;
+ int life;
+ int angle;
+ int x, y;
+} torp_t;
+
+
+
+void torp_init(void);
+void torp_create(client_t *, int);
+void torp_destroy(torp_t *);
+void torps_update(void);
+
+
+
+list_t torps_list;
+
+
+
+#endif
--- /dev/null
+/* $Id: trigonometry.c,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#include <math.h>
+
+#include <mmlog.h>
+
+#include <trigonometry.h>
+
+
+
+double sin_table[256], cos_table[256];
+
+
+
+void
+trigonometry_init(void)
+{
+ int i;
+ double f;
+
+ /* Initialize trigonometric tables. 0 points rightwards. */
+ for (i = 0; i < 256; i++) {
+ f = ((2 * M_PI) / 256) * i;
+ sin_table[i] = sin(f);
+ cos_table[i] = cos(f);
+ }
+}
+
+int
+angle_bounce(int angle)
+{
+
+ DEBUG_ASSERT(angle > -1 && angle < 256);
+
+ /*
+ * XXX
+ * 128 means perpendicular angle bouncing.
+ * We shoud actually offset by 64 etc as needed too...
+ * Should we be told via another parameter the relative angle of
+ * the object we had collision with? Or the direction?
+ */
+ return ((angle + 128) & 0xff);
+}
--- /dev/null
+/* $Id: trigonometry.h,v 1.1 2006/12/31 08:32:40 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+
+
+#ifndef _TRIGONOMETRY_H_
+#define _TRIGONOMETRY_H_
+
+
+
+#define VECTOR_X(x, a, r) ((int)(x) + (cos_table[(a)] * (r)))
+#define VECTOR_Y(y, a, r) ((int)(y) + (sin_table[(a)] * (r)))
+
+
+
+void trigonometry_init(void);
+int angle_bounce(int);
+
+
+
+extern double sin_table[256], cos_table[256];
+
+
+
+#endif