Initial import of TMS in public CVS repository (used to be private)
authorMatthew Mondor <mmondor@pulsar-zone.net>
Sun, 31 Dec 2006 08:32:40 +0000 (08:32 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Sun, 31 Dec 2006 08:32:40 +0000 (08:32 +0000)
113 files changed:
mmsoftware/mystic_ships/LICENSE [new file with mode: 0644]
mmsoftware/mystic_ships/README [new file with mode: 0644]
mmsoftware/mystic_ships/client/GNUmakefile [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/FedCA.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/RomDD.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/fedship.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/indship.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/kliship.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp [new file with mode: 0644]
mmsoftware/mystic_ships/client/bmp/oriship.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/romship.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/sbexpl.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/bmp/shexpl.bmp [new file with mode: 0755]
mmsoftware/mystic_ships/client/fnt/10x20.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/5x7.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/5x8.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x10.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x12.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x13.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x13B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x13O.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/6x9.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/7x13.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/7x13B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/7x13O.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/7x14.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/7x14B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/8x13.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/8x13B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/8x13O.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/9x15.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/9x15B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/9x18.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/fnt/9x18B.fnt [new file with mode: 0644]
mmsoftware/mystic_ships/client/ogg/1.ogg [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/conf.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/debug.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/debug.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/decode.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/decode.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/dlist.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/enc.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/enc.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/encode.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/endian.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/main.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/main.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/packets.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/packets.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/pool.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/pool.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/rawobjs.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/recvq.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/recvq.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/screen.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/screen.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_msg.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_msg.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_net_recv.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_net_recv.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_net_send.c [new file with mode: 0644]
mmsoftware/mystic_ships/client/src/thread_net_send.h [new file with mode: 0644]
mmsoftware/mystic_ships/client/wav/nt_cloaked.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_shield_down.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_shield_up.wav [new file with mode: 0755]
mmsoftware/mystic_ships/client/wav/nt_uncloak.wav [new file with mode: 0755]
mmsoftware/mystic_ships/common/encrypt_mmenc.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/encrypt_rc4.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/hmac.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/hmac.h [new file with mode: 0644]
mmsoftware/mystic_ships/common/hmac_rmd160.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/hmac_sha1.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/mmenc.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/mmenc.h [new file with mode: 0644]
mmsoftware/mystic_ships/common/packets_common.h [new file with mode: 0644]
mmsoftware/mystic_ships/common/rc4.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/rc4.h [new file with mode: 0644]
mmsoftware/mystic_ships/common/rmd160.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/rmd160.h [new file with mode: 0644]
mmsoftware/mystic_ships/common/sha1.c [new file with mode: 0644]
mmsoftware/mystic_ships/common/sha1.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/GNUmakefile [new file with mode: 0644]
mmsoftware/mystic_ships/server/README [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/client.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/client.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/conf.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/daemon.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/daemon.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/enc.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/enc.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/kqueue.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/kqueue.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/main.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/net.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/net.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/packets.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/packets.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/recvq.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/recvq.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/sendq.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/sendq.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/ships.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/ships.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/torp.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/torp.h [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/trigonometry.c [new file with mode: 0644]
mmsoftware/mystic_ships/server/src/trigonometry.h [new file with mode: 0644]

diff --git a/mmsoftware/mystic_ships/LICENSE b/mmsoftware/mystic_ships/LICENSE
new file mode 100644 (file)
index 0000000..a2baaa3
--- /dev/null
@@ -0,0 +1,34 @@
+/* $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.
+ */
diff --git a/mmsoftware/mystic_ships/README b/mmsoftware/mystic_ships/README
new file mode 100644 (file)
index 0000000..ef0d54f
--- /dev/null
@@ -0,0 +1,284 @@
+$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
diff --git a/mmsoftware/mystic_ships/client/GNUmakefile b/mmsoftware/mystic_ships/client/GNUmakefile
new file mode 100644 (file)
index 0000000..a681414
--- /dev/null
@@ -0,0 +1,106 @@
+# $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
diff --git a/mmsoftware/mystic_ships/client/bmp/FedCA.bmp b/mmsoftware/mystic_ships/client/bmp/FedCA.bmp
new file mode 100644 (file)
index 0000000..be3a562
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/FedCA.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/RomDD.bmp b/mmsoftware/mystic_ships/client/bmp/RomDD.bmp
new file mode 100644 (file)
index 0000000..50e429a
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/RomDD.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/fedship.bmp b/mmsoftware/mystic_ships/client/bmp/fedship.bmp
new file mode 100755 (executable)
index 0000000..465806f
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/fedship.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/indship.bmp b/mmsoftware/mystic_ships/client/bmp/indship.bmp
new file mode 100755 (executable)
index 0000000..1a7e39b
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/indship.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/kliship.bmp b/mmsoftware/mystic_ships/client/bmp/kliship.bmp
new file mode 100755 (executable)
index 0000000..cf52598
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/kliship.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp
new file mode 100644 (file)
index 0000000..c55b172
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--13-120-75-75-c-80-iso8859-1.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp
new file mode 100644 (file)
index 0000000..5c2ab05
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/misc-fixed-bold-r-normal--15-120-100-100-c-90-iso8859-1.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp
new file mode 100644 (file)
index 0000000..e8ac5d4
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--13-120-75-75-c-80-iso8859-1.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp
new file mode 100644 (file)
index 0000000..9e1d9f2
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/misc-fixed-medium-r-normal--15-120-100-100-c-90-iso8859-1.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/oriship.bmp b/mmsoftware/mystic_ships/client/bmp/oriship.bmp
new file mode 100755 (executable)
index 0000000..166fb1a
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/oriship.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/romship.bmp b/mmsoftware/mystic_ships/client/bmp/romship.bmp
new file mode 100755 (executable)
index 0000000..753d38a
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/romship.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp b/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp
new file mode 100755 (executable)
index 0000000..b06ffcf
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/sbexpl.bmp differ
diff --git a/mmsoftware/mystic_ships/client/bmp/shexpl.bmp b/mmsoftware/mystic_ships/client/bmp/shexpl.bmp
new file mode 100755 (executable)
index 0000000..72a62c4
Binary files /dev/null and b/mmsoftware/mystic_ships/client/bmp/shexpl.bmp differ
diff --git a/mmsoftware/mystic_ships/client/fnt/10x20.fnt b/mmsoftware/mystic_ships/client/fnt/10x20.fnt
new file mode 100644 (file)
index 0000000..99b3486
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/10x20.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/5x7.fnt b/mmsoftware/mystic_ships/client/fnt/5x7.fnt
new file mode 100644 (file)
index 0000000..3612165
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/5x7.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/5x8.fnt b/mmsoftware/mystic_ships/client/fnt/5x8.fnt
new file mode 100644 (file)
index 0000000..01add72
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/5x8.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x10.fnt b/mmsoftware/mystic_ships/client/fnt/6x10.fnt
new file mode 100644 (file)
index 0000000..3c49f20
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x10.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x12.fnt b/mmsoftware/mystic_ships/client/fnt/6x12.fnt
new file mode 100644 (file)
index 0000000..156865b
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x12.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x13.fnt b/mmsoftware/mystic_ships/client/fnt/6x13.fnt
new file mode 100644 (file)
index 0000000..e259f1a
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x13.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x13B.fnt b/mmsoftware/mystic_ships/client/fnt/6x13B.fnt
new file mode 100644 (file)
index 0000000..503b773
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x13B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x13O.fnt b/mmsoftware/mystic_ships/client/fnt/6x13O.fnt
new file mode 100644 (file)
index 0000000..cb5a27f
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x13O.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/6x9.fnt b/mmsoftware/mystic_ships/client/fnt/6x9.fnt
new file mode 100644 (file)
index 0000000..cca4689
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/6x9.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/7x13.fnt b/mmsoftware/mystic_ships/client/fnt/7x13.fnt
new file mode 100644 (file)
index 0000000..d20852e
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/7x13.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/7x13B.fnt b/mmsoftware/mystic_ships/client/fnt/7x13B.fnt
new file mode 100644 (file)
index 0000000..9f3fbed
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/7x13B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/7x13O.fnt b/mmsoftware/mystic_ships/client/fnt/7x13O.fnt
new file mode 100644 (file)
index 0000000..e5b7732
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/7x13O.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/7x14.fnt b/mmsoftware/mystic_ships/client/fnt/7x14.fnt
new file mode 100644 (file)
index 0000000..fdcd752
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/7x14.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/7x14B.fnt b/mmsoftware/mystic_ships/client/fnt/7x14B.fnt
new file mode 100644 (file)
index 0000000..939aa63
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/7x14B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/8x13.fnt b/mmsoftware/mystic_ships/client/fnt/8x13.fnt
new file mode 100644 (file)
index 0000000..9de72b4
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/8x13.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/8x13B.fnt b/mmsoftware/mystic_ships/client/fnt/8x13B.fnt
new file mode 100644 (file)
index 0000000..96179a0
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/8x13B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/8x13O.fnt b/mmsoftware/mystic_ships/client/fnt/8x13O.fnt
new file mode 100644 (file)
index 0000000..a38a371
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/8x13O.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/9x15.fnt b/mmsoftware/mystic_ships/client/fnt/9x15.fnt
new file mode 100644 (file)
index 0000000..349e5bd
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/9x15.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/9x15B.fnt b/mmsoftware/mystic_ships/client/fnt/9x15B.fnt
new file mode 100644 (file)
index 0000000..23bc97e
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/9x15B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/9x18.fnt b/mmsoftware/mystic_ships/client/fnt/9x18.fnt
new file mode 100644 (file)
index 0000000..0651c97
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/9x18.fnt differ
diff --git a/mmsoftware/mystic_ships/client/fnt/9x18B.fnt b/mmsoftware/mystic_ships/client/fnt/9x18B.fnt
new file mode 100644 (file)
index 0000000..d93d09d
Binary files /dev/null and b/mmsoftware/mystic_ships/client/fnt/9x18B.fnt differ
diff --git a/mmsoftware/mystic_ships/client/ogg/1.ogg b/mmsoftware/mystic_ships/client/ogg/1.ogg
new file mode 100644 (file)
index 0000000..44810a8
Binary files /dev/null and b/mmsoftware/mystic_ships/client/ogg/1.ogg differ
diff --git a/mmsoftware/mystic_ships/client/src/conf.h b/mmsoftware/mystic_ships/client/src/conf.h
new file mode 100644 (file)
index 0000000..4cb04d8
--- /dev/null
@@ -0,0 +1,31 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/debug.c b/mmsoftware/mystic_ships/client/src/debug.c
new file mode 100644 (file)
index 0000000..6c5fe44
--- /dev/null
@@ -0,0 +1,42 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/debug.h b/mmsoftware/mystic_ships/client/src/debug.h
new file mode 100644 (file)
index 0000000..e89f851
--- /dev/null
@@ -0,0 +1,44 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/decode.c b/mmsoftware/mystic_ships/client/src/decode.c
new file mode 100644 (file)
index 0000000..36f2fff
--- /dev/null
@@ -0,0 +1,38 @@
+/* $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;
+       }
+}
diff --git a/mmsoftware/mystic_ships/client/src/decode.h b/mmsoftware/mystic_ships/client/src/decode.h
new file mode 100644 (file)
index 0000000..ac8ef6c
--- /dev/null
@@ -0,0 +1,23 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/dlist.h b/mmsoftware/mystic_ships/client/src/dlist.h
new file mode 100644 (file)
index 0000000..9c368e0
--- /dev/null
@@ -0,0 +1,174 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/enc.c b/mmsoftware/mystic_ships/client/src/enc.c
new file mode 100644 (file)
index 0000000..99b07ad
--- /dev/null
@@ -0,0 +1,27 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/enc.h b/mmsoftware/mystic_ships/client/src/enc.h
new file mode 100644 (file)
index 0000000..1788efd
--- /dev/null
@@ -0,0 +1,22 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/encode.c b/mmsoftware/mystic_ships/client/src/encode.c
new file mode 100644 (file)
index 0000000..f5ce243
--- /dev/null
@@ -0,0 +1,107 @@
+/* $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;
+       }
+}
diff --git a/mmsoftware/mystic_ships/client/src/endian.h b/mmsoftware/mystic_ships/client/src/endian.h
new file mode 100644 (file)
index 0000000..a0f55ec
--- /dev/null
@@ -0,0 +1,52 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/main.c b/mmsoftware/mystic_ships/client/src/main.c
new file mode 100644 (file)
index 0000000..4362e88
--- /dev/null
@@ -0,0 +1,852 @@
+/* $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);
+       }
+}
diff --git a/mmsoftware/mystic_ships/client/src/main.h b/mmsoftware/mystic_ships/client/src/main.h
new file mode 100644 (file)
index 0000000..9ec699c
--- /dev/null
@@ -0,0 +1,40 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/packets.c b/mmsoftware/mystic_ships/client/src/packets.c
new file mode 100644 (file)
index 0000000..850872a
--- /dev/null
@@ -0,0 +1,450 @@
+/* $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));
+}
diff --git a/mmsoftware/mystic_ships/client/src/packets.h b/mmsoftware/mystic_ships/client/src/packets.h
new file mode 100644 (file)
index 0000000..b399545
--- /dev/null
@@ -0,0 +1,57 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/pool.c b/mmsoftware/mystic_ships/client/src/pool.c
new file mode 100644 (file)
index 0000000..65f6ac3
--- /dev/null
@@ -0,0 +1,417 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/client/src/pool.h b/mmsoftware/mystic_ships/client/src/pool.h
new file mode 100644 (file)
index 0000000..b34a248
--- /dev/null
@@ -0,0 +1,122 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/rawobjs.h b/mmsoftware/mystic_ships/client/src/rawobjs.h
new file mode 100644 (file)
index 0000000..dc993d1
--- /dev/null
@@ -0,0 +1,79 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/recvq.c b/mmsoftware/mystic_ships/client/src/recvq.c
new file mode 100644 (file)
index 0000000..3596f2f
--- /dev/null
@@ -0,0 +1,318 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/client/src/recvq.h b/mmsoftware/mystic_ships/client/src/recvq.h
new file mode 100644 (file)
index 0000000..bde3d13
--- /dev/null
@@ -0,0 +1,44 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/screen.c b/mmsoftware/mystic_ships/client/src/screen.c
new file mode 100644 (file)
index 0000000..5617a63
--- /dev/null
@@ -0,0 +1,65 @@
+/* $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();
+}
diff --git a/mmsoftware/mystic_ships/client/src/screen.h b/mmsoftware/mystic_ships/client/src/screen.h
new file mode 100644 (file)
index 0000000..1c901a8
--- /dev/null
@@ -0,0 +1,34 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/thread_msg.c b/mmsoftware/mystic_ships/client/src/thread_msg.c
new file mode 100644 (file)
index 0000000..c458dae
--- /dev/null
@@ -0,0 +1,456 @@
+/* $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());
+}
diff --git a/mmsoftware/mystic_ships/client/src/thread_msg.h b/mmsoftware/mystic_ships/client/src/thread_msg.h
new file mode 100644 (file)
index 0000000..38b71ec
--- /dev/null
@@ -0,0 +1,91 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/thread_net_recv.c b/mmsoftware/mystic_ships/client/src/thread_net_recv.c
new file mode 100644 (file)
index 0000000..3f593e5
--- /dev/null
@@ -0,0 +1,188 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/client/src/thread_net_recv.h b/mmsoftware/mystic_ships/client/src/thread_net_recv.h
new file mode 100644 (file)
index 0000000..09f8811
--- /dev/null
@@ -0,0 +1,65 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/src/thread_net_send.c b/mmsoftware/mystic_ships/client/src/thread_net_send.c
new file mode 100644 (file)
index 0000000..0d3a73f
--- /dev/null
@@ -0,0 +1,129 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/client/src/thread_net_send.h b/mmsoftware/mystic_ships/client/src/thread_net_send.h
new file mode 100644 (file)
index 0000000..7eec9ef
--- /dev/null
@@ -0,0 +1,35 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav b/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav
new file mode 100755 (executable)
index 0000000..3e010dd
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_cloaked.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav b/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav
new file mode 100755 (executable)
index 0000000..b7ba8cb
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_explosion_other.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav b/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav
new file mode 100755 (executable)
index 0000000..dddf2c6
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_fire_torp_other.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav b/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav
new file mode 100755 (executable)
index 0000000..73ecd7c
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_plasma_hit.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav b/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav
new file mode 100755 (executable)
index 0000000..0888f08
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_shield_down.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav b/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav
new file mode 100755 (executable)
index 0000000..909f72c
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_shield_up.wav differ
diff --git a/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav b/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav
new file mode 100755 (executable)
index 0000000..e4df979
Binary files /dev/null and b/mmsoftware/mystic_ships/client/wav/nt_uncloak.wav differ
diff --git a/mmsoftware/mystic_ships/common/encrypt_mmenc.c b/mmsoftware/mystic_ships/common/encrypt_mmenc.c
new file mode 100644 (file)
index 0000000..f2a265c
--- /dev/null
@@ -0,0 +1,116 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/common/encrypt_rc4.c b/mmsoftware/mystic_ships/common/encrypt_rc4.c
new file mode 100644 (file)
index 0000000..306845e
--- /dev/null
@@ -0,0 +1,116 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/common/hmac.c b/mmsoftware/mystic_ships/common/hmac.c
new file mode 100644 (file)
index 0000000..69d23f6
--- /dev/null
@@ -0,0 +1,137 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/common/hmac.h b/mmsoftware/mystic_ships/common/hmac.h
new file mode 100644 (file)
index 0000000..1567457
--- /dev/null
@@ -0,0 +1,13 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/hmac_rmd160.c b/mmsoftware/mystic_ships/common/hmac_rmd160.c
new file mode 100644 (file)
index 0000000..377241c
--- /dev/null
@@ -0,0 +1,31 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/hmac_sha1.c b/mmsoftware/mystic_ships/common/hmac_sha1.c
new file mode 100644 (file)
index 0000000..0a41b45
--- /dev/null
@@ -0,0 +1,31 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/mmenc.c b/mmsoftware/mystic_ships/common/mmenc.c
new file mode 100644 (file)
index 0000000..2bbc073
--- /dev/null
@@ -0,0 +1,302 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/common/mmenc.h b/mmsoftware/mystic_ships/common/mmenc.h
new file mode 100644 (file)
index 0000000..4bdfb91
--- /dev/null
@@ -0,0 +1,70 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/packets_common.h b/mmsoftware/mystic_ships/common/packets_common.h
new file mode 100644 (file)
index 0000000..421e9d7
--- /dev/null
@@ -0,0 +1,219 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/rc4.c b/mmsoftware/mystic_ships/common/rc4.c
new file mode 100644 (file)
index 0000000..e982479
--- /dev/null
@@ -0,0 +1,166 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/common/rc4.h b/mmsoftware/mystic_ships/common/rc4.h
new file mode 100644 (file)
index 0000000..3965dde
--- /dev/null
@@ -0,0 +1,66 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/common/rmd160.c b/mmsoftware/mystic_ships/common/rmd160.c
new file mode 100644 (file)
index 0000000..580cd04
--- /dev/null
@@ -0,0 +1,427 @@
+/* $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);
+       }
+}
diff --git a/mmsoftware/mystic_ships/common/rmd160.h b/mmsoftware/mystic_ships/common/rmd160.h
new file mode 100644 (file)
index 0000000..dc33460
--- /dev/null
@@ -0,0 +1,56 @@
+/* $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_ */
diff --git a/mmsoftware/mystic_ships/common/sha1.c b/mmsoftware/mystic_ships/common/sha1.c
new file mode 100644 (file)
index 0000000..7f1a07d
--- /dev/null
@@ -0,0 +1,271 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/common/sha1.h b/mmsoftware/mystic_ships/common/sha1.h
new file mode 100644 (file)
index 0000000..b119b0a
--- /dev/null
@@ -0,0 +1,40 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/GNUmakefile b/mmsoftware/mystic_ships/server/GNUmakefile
new file mode 100644 (file)
index 0000000..4a91bac
--- /dev/null
@@ -0,0 +1,25 @@
+# $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)
diff --git a/mmsoftware/mystic_ships/server/README b/mmsoftware/mystic_ships/server/README
new file mode 100644 (file)
index 0000000..43516e0
--- /dev/null
@@ -0,0 +1,48 @@
+$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.
diff --git a/mmsoftware/mystic_ships/server/src/client.c b/mmsoftware/mystic_ships/server/src/client.c
new file mode 100644 (file)
index 0000000..99e1107
--- /dev/null
@@ -0,0 +1,222 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/server/src/client.h b/mmsoftware/mystic_ships/server/src/client.h
new file mode 100644 (file)
index 0000000..b6d71bb
--- /dev/null
@@ -0,0 +1,63 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/conf.h b/mmsoftware/mystic_ships/server/src/conf.h
new file mode 100644 (file)
index 0000000..999e4b2
--- /dev/null
@@ -0,0 +1,45 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/daemon.c b/mmsoftware/mystic_ships/server/src/daemon.c
new file mode 100644 (file)
index 0000000..7756b62
--- /dev/null
@@ -0,0 +1,129 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/daemon.h b/mmsoftware/mystic_ships/server/src/daemon.h
new file mode 100644 (file)
index 0000000..46d7b66
--- /dev/null
@@ -0,0 +1,19 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/enc.c b/mmsoftware/mystic_ships/server/src/enc.c
new file mode 100644 (file)
index 0000000..b2a199f
--- /dev/null
@@ -0,0 +1,98 @@
+/* $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));
+}
diff --git a/mmsoftware/mystic_ships/server/src/enc.h b/mmsoftware/mystic_ships/server/src/enc.h
new file mode 100644 (file)
index 0000000..2c9a3a1
--- /dev/null
@@ -0,0 +1,22 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/kqueue.c b/mmsoftware/mystic_ships/server/src/kqueue.c
new file mode 100644 (file)
index 0000000..e783cc8
--- /dev/null
@@ -0,0 +1,305 @@
+/* $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++;
+}
diff --git a/mmsoftware/mystic_ships/server/src/kqueue.h b/mmsoftware/mystic_ships/server/src/kqueue.h
new file mode 100644 (file)
index 0000000..c692c02
--- /dev/null
@@ -0,0 +1,44 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/main.c b/mmsoftware/mystic_ships/server/src/main.c
new file mode 100644 (file)
index 0000000..a96c362
--- /dev/null
@@ -0,0 +1,72 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/net.c b/mmsoftware/mystic_ships/server/src/net.c
new file mode 100644 (file)
index 0000000..6206c2e
--- /dev/null
@@ -0,0 +1,131 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/net.h b/mmsoftware/mystic_ships/server/src/net.h
new file mode 100644 (file)
index 0000000..41963bd
--- /dev/null
@@ -0,0 +1,23 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/packets.c b/mmsoftware/mystic_ships/server/src/packets.c
new file mode 100644 (file)
index 0000000..2168a78
--- /dev/null
@@ -0,0 +1,543 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/packets.h b/mmsoftware/mystic_ships/server/src/packets.h
new file mode 100644 (file)
index 0000000..b45cb05
--- /dev/null
@@ -0,0 +1,52 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/recvq.c b/mmsoftware/mystic_ships/server/src/recvq.c
new file mode 100644 (file)
index 0000000..6cd296d
--- /dev/null
@@ -0,0 +1,358 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/recvq.h b/mmsoftware/mystic_ships/server/src/recvq.h
new file mode 100644 (file)
index 0000000..bc31155
--- /dev/null
@@ -0,0 +1,59 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/sendq.c b/mmsoftware/mystic_ships/server/src/sendq.c
new file mode 100644 (file)
index 0000000..e2e428c
--- /dev/null
@@ -0,0 +1,248 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/sendq.h b/mmsoftware/mystic_ships/server/src/sendq.h
new file mode 100644 (file)
index 0000000..49b47b1
--- /dev/null
@@ -0,0 +1,57 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/ships.c b/mmsoftware/mystic_ships/server/src/ships.c
new file mode 100644 (file)
index 0000000..e341fce
--- /dev/null
@@ -0,0 +1,244 @@
+/* $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;
+}
diff --git a/mmsoftware/mystic_ships/server/src/ships.h b/mmsoftware/mystic_ships/server/src/ships.h
new file mode 100644 (file)
index 0000000..2c3b595
--- /dev/null
@@ -0,0 +1,62 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/torp.c b/mmsoftware/mystic_ships/server/src/torp.c
new file mode 100644 (file)
index 0000000..2680216
--- /dev/null
@@ -0,0 +1,116 @@
+/* $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);
+               }
+       }
+}
diff --git a/mmsoftware/mystic_ships/server/src/torp.h b/mmsoftware/mystic_ships/server/src/torp.h
new file mode 100644 (file)
index 0000000..4fa2aec
--- /dev/null
@@ -0,0 +1,44 @@
+/* $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
diff --git a/mmsoftware/mystic_ships/server/src/trigonometry.c b/mmsoftware/mystic_ships/server/src/trigonometry.c
new file mode 100644 (file)
index 0000000..bf2fced
--- /dev/null
@@ -0,0 +1,50 @@
+/* $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);
+}
diff --git a/mmsoftware/mystic_ships/server/src/trigonometry.h b/mmsoftware/mystic_ships/server/src/trigonometry.h
new file mode 100644 (file)
index 0000000..f34b447
--- /dev/null
@@ -0,0 +1,29 @@
+/* $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