From: Matthew Mondor Date: Sun, 31 Dec 2006 08:32:40 +0000 (+0000) Subject: Initial import of TMS in public CVS repository (used to be private) X-Git-Tag: pgsql-branch-merge~55 X-Git-Url: http://git.pulsar-zone.net/?a=commitdiff_plain;h=f21437ba9450ed59ab32a11ba6b556f249677e18;p=mmondor.git Initial import of TMS in public CVS repository (used to be private) --- diff --git a/mmsoftware/mystic_ships/LICENSE b/mmsoftware/mystic_ships/LICENSE new file mode 100644 index 0000000..a2baaa3 --- /dev/null +++ b/mmsoftware/mystic_ships/LICENSE @@ -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 index 0000000..ef0d54f --- /dev/null +++ b/mmsoftware/mystic_ships/README @@ -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 index 0000000..a681414 --- /dev/null +++ b/mmsoftware/mystic_ships/client/GNUmakefile @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 index 0000000..4cb04d8 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/conf.h @@ -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 index 0000000..6c5fe44 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/debug.c @@ -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 +#include +#include + + + +#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 index 0000000..e89f851 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/debug.h @@ -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 index 0000000..36f2fff --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/decode.c @@ -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 +#include + +#include + + + +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 index 0000000..ac8ef6c --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/decode.h @@ -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 index 0000000..9c368e0 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/dlist.h @@ -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 index 0000000..99b07ad --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/enc.c @@ -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 + + + +#ifdef USE_ENCRYPTION + + + +#include + + + +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 index 0000000..1788efd --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/enc.h @@ -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 + + + +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 index 0000000..f5ce243 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/encode.c @@ -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 +#include +#include + + + +#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 \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 index 0000000..a0f55ec --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/endian.h @@ -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 + +#include +#include + + + +#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 index 0000000..4362e88 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/main.c @@ -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 +#include +#include +#include +#include +/* XXX UNIX DEBUG */ +#include + +/* THIRD PARTY LIBRARY HEADERS */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* APPLICATION HEADERS */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* 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 index 0000000..9ec699c --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/main.h @@ -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 + +#include + + + +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 index 0000000..850872a --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/packets.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +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 index 0000000..b399545 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/packets.h @@ -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 +#include +#include + + + +/* + * 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 index 0000000..65f6ac3 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/pool.c @@ -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 +#include +#include + +#include +#include +#include + + + +/* 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); + } + + /* + * 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 index 0000000..b34a248 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/pool.h @@ -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 + +#include + + + +/* 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 index 0000000..dc993d1 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/rawobjs.h @@ -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 index 0000000..3596f2f --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/recvq.c @@ -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 +#include +#include + +#include + +#include + +#ifdef USE_COMPRESSION +#include +#endif + +#include +#include +#ifdef USE_ENCRYPTION +#include +#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 index 0000000..bde3d13 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/recvq.h @@ -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 + +#include +#include + +#include + + + +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 index 0000000..5617a63 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/screen.c @@ -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 +#include + +#include + +#include +#include + + + +#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 index 0000000..1c901a8 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/screen.h @@ -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 + + + +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 index 0000000..c458dae --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_msg.c @@ -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 + +#include +#include +#include + + + +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 index 0000000..38b71ec --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_msg.h @@ -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 + +#include +#include + +#include +#include + + + +#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 index 0000000..3f593e5 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_net_recv.c @@ -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 +#include +#include + +#include +#include +#include +#include + + + +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 index 0000000..09f8811 --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_net_recv.h @@ -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 +#include + +#include + + + +/* + * 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. + * will be 0 on success or -1 on failure, in which case the error + * message string will be found into . + */ +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 size, with a NULL 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 index 0000000..0d3a73f --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_net_send.c @@ -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 +#include + +#include + +#ifdef USE_COMPRESSION +#include +#endif + +#include +#include +#ifdef USE_ENCRYPTION +#include +#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 index 0000000..7eec9ef --- /dev/null +++ b/mmsoftware/mystic_ships/client/src/thread_net_send.h @@ -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 +#include + +#include + + + +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 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 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 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 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 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 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 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 index 0000000..f2a265c --- /dev/null +++ b/mmsoftware/mystic_ships/common/encrypt_mmenc.c @@ -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 +#include +#include +#include + +#include + + + +#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 index 0000000..306845e --- /dev/null +++ b/mmsoftware/mystic_ships/common/encrypt_rc4.c @@ -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 +#include +#include +#include + +#include + + + +#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 index 0000000..69d23f6 --- /dev/null +++ b/mmsoftware/mystic_ships/common/hmac.c @@ -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 +#include +#include + +#include + + + +/* 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 index 0000000..1567457 --- /dev/null +++ b/mmsoftware/mystic_ships/common/hmac.h @@ -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 index 0000000..377241c --- /dev/null +++ b/mmsoftware/mystic_ships/common/hmac_rmd160.c @@ -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 + + + +#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 + + + +#endif diff --git a/mmsoftware/mystic_ships/common/hmac_sha1.c b/mmsoftware/mystic_ships/common/hmac_sha1.c new file mode 100644 index 0000000..0a41b45 --- /dev/null +++ b/mmsoftware/mystic_ships/common/hmac_sha1.c @@ -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 + + + +#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 + + + +#endif diff --git a/mmsoftware/mystic_ships/common/mmenc.c b/mmsoftware/mystic_ships/common/mmenc.c new file mode 100644 index 0000000..2bbc073 --- /dev/null +++ b/mmsoftware/mystic_ships/common/mmenc.c @@ -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 +#include + +#include + + + +/* + * 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 index 0000000..4bdfb91 --- /dev/null +++ b/mmsoftware/mystic_ships/common/mmenc.h @@ -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 +#include + + + +#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 index 0000000..421e9d7 --- /dev/null +++ b/mmsoftware/mystic_ships/common/packets_common.h @@ -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 + + + +/* + * 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 index 0000000..e982479 --- /dev/null +++ b/mmsoftware/mystic_ships/common/rc4.c @@ -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 +#include + +#include + + + +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 index 0000000..3965dde --- /dev/null +++ b/mmsoftware/mystic_ships/common/rc4.h @@ -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 + + + +#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 index 0000000..580cd04 --- /dev/null +++ b/mmsoftware/mystic_ships/common/rmd160.c @@ -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 +#include + +#ifdef __NetBSD__ +#include +#endif + +#include + + + +#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 index 0000000..dc33460 --- /dev/null +++ b/mmsoftware/mystic_ships/common/rmd160.h @@ -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 + + + +#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 index 0000000..7f1a07d --- /dev/null +++ b/mmsoftware/mystic_ships/common/sha1.c @@ -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 + * 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 +#include + +#ifdef __NetBSD__ +#include +#endif + +#include + + + +#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 index 0000000..b119b0a --- /dev/null +++ b/mmsoftware/mystic_ships/common/sha1.h @@ -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 + * 100% Public Domain + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + + + +#include + + + +#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 index 0000000..4a91bac --- /dev/null +++ b/mmsoftware/mystic_ships/server/GNUmakefile @@ -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 index 0000000..43516e0 --- /dev/null +++ b/mmsoftware/mystic_ships/server/README @@ -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 index 0000000..99e1107 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/client.c @@ -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 +#include +#include +#include +#include + +#include +#include +#include + + + +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 index 0000000..b6d71bb --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/client.h @@ -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 +#include +#include + +#include +#include + +#include +#include +#include +#include + + + +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 index 0000000..999e4b2 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/conf.h @@ -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 index 0000000..7756b62 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/daemon.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + + +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 index 0000000..46d7b66 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/daemon.h @@ -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 index 0000000..b2a199f --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/enc.c @@ -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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include /* current_time */ +#include + + + +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 index 0000000..2c9a3a1 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/enc.h @@ -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 + + + +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 index 0000000..e783cc8 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/kqueue.c @@ -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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + + + +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 index 0000000..c692c02 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/kqueue.h @@ -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 +#include + + + +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 index 0000000..a96c362 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/main.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +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 index 0000000..6206c2e --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/net.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + + + +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 index 0000000..41963bd --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/net.h @@ -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 index 0000000..2168a78 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/packets.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +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 index 0000000..b45cb05 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/packets.h @@ -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 +#include +#include +#include + + + +/* + * 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 index 0000000..6cd296d --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/recvq.c @@ -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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef USE_ENCRYPTION +#include +#endif + +#include +#include + +#ifdef USE_COMPRESSION +#include +#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 index 0000000..bc31155 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/recvq.h @@ -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 + +#include + +#ifdef USE_COMPRESSION +#include +#endif + +#ifdef USE_ENCRYPTION +#include +#include +#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 index 0000000..e2e428c --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/sendq.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef USE_ENCRYPTION +#include +#endif + +#ifdef USE_COMPRESSION +#include +#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 index 0000000..49b47b1 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/sendq.h @@ -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 + +#include + +#ifdef USE_COMPRESSION +#include +#endif + +#include + +#ifdef USE_ENCRYPTION +#include +#include +#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 index 0000000..e341fce --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/ships.c @@ -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 +#include + +#include + +#include +#include +#include + + + +/* 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 index 0000000..2c3b595 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/ships.h @@ -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 index 0000000..2680216 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/torp.c @@ -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 +#include + +#include + +#include +#include +#include + + + +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 index 0000000..4fa2aec --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/torp.h @@ -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 + + + +/* + * 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 index 0000000..bf2fced --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/trigonometry.c @@ -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 + +#include + +#include + + + +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 index 0000000..f34b447 --- /dev/null +++ b/mmsoftware/mystic_ships/server/src/trigonometry.h @@ -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