mmpong-0.9.1/0000755000175000017500000000000011354734770011775 5ustar andreandremmpong-0.9.1/Makefile.inner0000644000175000017500000000553211132130740014531 0ustar andreandre-include Makefile.def -include .Makefile.depend .PHONY: all FORCE #Default target all: lib$(NAME).so $(NAME)d $(NAME) $(NAME)-gl #Find sources and create list of corresponding object targets: find_sources = $(subst ./,,$(shell find . -name "*$(1)" -and -path "./$(2)/*")) add_objects = $(addprefix $(OBJDIR)/, $(patsubst %$(1),%.o,$(call find_sources,$(1),$(2)))) C_COMMON_OBJECTS = $(call add_objects,.c,common) C_SERVER_OBJECTS = $(call add_objects,.c,server) C_CLIENT_CACA_OBJECTS = $(call add_objects,.c,client_caca) CPP_CLIENT_GL_OBJECTS = $(call add_objects,.cpp,client_gl) C_LIB_OBJECTS = $(call add_objects,.c,lib) SERVER_OBJECTS = $(C_COMMON_OBJECTS) $(C_SERVER_OBJECTS) CLIENT_CACA_OBJECTS = $(C_COMMON_OBJECTS) $(C_CLIENT_CACA_OBJECTS) CLIENT_GL_OBJECTS = $(C_COMMON_OBJECTS) $(CPP_CLIENT_GL_OBJECTS) LIB_OBJECTS = $(C_COMMON_OBJECTS) $(C_LIB_OBJECTS) OBJDIRS = $(sort $(dir $(SERVER_OBJECTS) $(CLIENT_CACA_OBJECTS) $(CLIENT_GL_OBJECTS) $(LIB_OBJECTS))) #Create dirs $(OBJDIRS): @mkdir -p $@ #Compile sources resolve_source = $(patsubst $(OBJDIR)/%,%,$(patsubst %.o,%$(1),$@)) compile = @$(ECHO) -e '\033[32mCompiling $(3) file $<\033[31m';\ (($(1) -c $(2) -o $@ $<) && $(RST)) || ($(RST) && $(FAIL)) $(C_SERVER_OBJECTS): $(call resolve_source,.c) $(call compile,$(CC),$(SERVER_CFLAGS),server) $(C_CLIENT_CACA_OBJECTS): $(call resolve_source,.c) $(call compile,$(CC),$(CLIENT_CACA_CFLAGS),client-caca) $(CPP_CLIENT_GL_OBJECTS): $(call resolve_source,.cpp) $(call compile,$(CXX),$(CLIENT_GL_CXXFLAGS),client-caca) $(C_LIB_OBJECTS): $(call resolve_source,.c) $(call compile,$(CC),$(LIB_CFLAGS),library) $(C_COMMON_OBJECTS): $(call resolve_source,.c) $(call compile,$(CC),$(COMMON_CFLAGS),common) #Link objects $(NAME)d: lib$(NAME).so $(OBJDIRS) $(SERVER_OBJECTS) ifneq ("$(strip $(C_SERVER_OBJECTS))","") @$(ECHO) Linking the server @$(LD) -L. -lmmpong $(SERVER_LDLIBS) $(LDFLAGS) $(SERVER_LDSTAT) -o $@ $(SERVER_OBJECTS) else @$(ECHO) -e 'Nothing to make the server from.\n' endif $(NAME): lib$(NAME).so $(OBJDIRS) $(CLIENT_CACA_OBJECTS) ifneq ("$(strip $(C_CLIENT_CACA_OBJECTS))","") @$(ECHO) Linking client-caca @$(LD) -L. -lmmpong $(CLIENT_CACA_LDLIBS) $(LDFLAGS) $(CLIENT_CACA_LDSTAT) -o $@ $(CLIENT_CACA_OBJECTS) else @$(ECHO) -e 'Nothing to make the client_caca from.\n' endif $(NAME)-gl: lib$(NAME).so $(OBJDIRS) $(CLIENT_GL_OBJECTS) ifneq ("$(strip $(CPP_CLIENT_GL_OBJECTS))","") @$(ECHO) Linking client-gl @$(LD) -L. -lmmpong $(CLIENT_GL_LDLIBS) $(LDFLAGS) $(CLIENT_GL_LDSTAT) -o $@ $(CLIENT_GL_OBJECTS) else @$(ECHO) -e 'Nothing to make the client_gl from.\n' endif lib$(NAME).so: $(OBJDIRS) $(LIB_OBJECTS) ifneq ("$(strip $(C_LIB_OBJECTS))","") @$(ECHO) Linking the libmmpong library @$(LD) -shared $(LIB_LDLIBS) $(LDFLAGS) $(LIB_LDSTAT) -o $@ $(LIB_OBJECTS) else @$(ECHO) -e 'Nothing to make the lib from.\n' endif #FORCE target... do not remove FORCE: mmpong-0.9.1/Makefile0000644000175000017500000000551711131704043013424 0ustar andreandre-include Makefile.def .PHONY: all clean FORCE depend lib daemon server client_caca client_gl #Aliases .DEFAULT_GOAL := everything everything: all lib: lib$(NAME).so daemon server: $(NAME)d client-caca: $(NAME) client-gl: $(NAME)-gl #FORCE target... do not remove FORCE: H_CONFIGS = $(subst ./,,$(shell find . -name "config*.h.in" -and -not -path './test/*')) $(H_CONFIGS:.h.in=.h): FORCE $(@:.h=.h.in) @if [ ! -f .configured ]; then\ $(ECHO) -e '\033[32mConfiguring';\ $(RST);\ fi @sed -e "s/\$${SVN_REV}/${SVN_REV}/g"\ -e "s/\$${SERVER_RES_PATH}/${SERVER_RES_PATH}/g"\ -e "s/\$${LIB_RES_PATH}/${LIB_RES_PATH}/g"\ -e "s/\$${CLIENT_GL_RES_PATH}/${CLIENT_GL_RES_PATH}/g"\ -e "s/\$${CLIENT_CACA_RES_PATH}/${CLIENT_CACA_RES_PATH}/g"\ $@.in >$@.temp @cmp -s $@.temp $@ || (if [ ! -f .configured ]; then echo "Revision update detected"; fi; mv $@.temp $@) @touch .configured @rm -f $@.temp sizeof.h: @($(CC) -o test/sizeof test/sizeof.c) && (test/sizeof > sizeof.h) # source list: C_SOURCES = $(subst ./,,$(shell find . -name "*.c" -and -not -path './test/*')) CC_SOURCES = $(subst ./,,$(shell find . -name "*.cpp" -and -not -path './test/*')) #compiler and linker calls respecting dependencies all $(NAME)d $(NAME) $(NAME)-gl lib$(NAME).so: sizeof.h depend FORCE $(MAKE) $(MAKEFLAGS) -f Makefile.inner $@ help: @$(ECHO) -e "\nMake targets:" @$(ECHO) -e "lib\t(a.k.a. libmmpong.so)\n\t-- Builds the mmpong shared library, on which all the other targets are based" @$(ECHO) -e "server\t(a.k.a. daemon, mmpongd)\n\t-- Builds the daemon binary" @$(ECHO) -e "client-caca\t(a.k.a. mmpong)\n\t-- Builds the libcaca-based client" @$(ECHO) -e "client-gl\t(a.k.a. mmpong-gl)\n\t-- Builds the SDL-based client" @$(ECHO) -e "flags\n\t-- Show the compiler flags to be used during the build" flags: @$(ECHO) -e '\nCompiles with SERVER_CFLAGS:\n$(SERVER_CFLAGS)\n' @$(ECHO) -e 'Links with SERVER_LDLIBS:\n$(SERVER_LDLIBS)\n' @$(ECHO) -e '\nCompiles with CLIENT_CACA_CFLAGS:\n$(CLIENT_CACA_CFLAGS)\n' @$(ECHO) -e 'Links with CLIENT_CACA_LDLIBS:\n$(CLIENT_CACA_LDLIBS)\n' @$(ECHO) -e '\nCompiles with CLIENT_GL_CFLAGS:\n$(CLIENT_GL_CFLAGS)\n' @$(ECHO) -e 'Links with CLIENT_GL_LDLIBS:\n$(CLIENT_GL_LDLIBS)\n' #dependency resolver depend: FORCE $(H_CONFIGS:.h.in=.h) @$(ECHO) -e '\033[32mResolving dependencies\033[31m' @rm -f .configured .Makefile.depend ifneq ("$(strip $(C_SOURCES))","") @$(CC) $(ALL_CFLAGS) -I. -MM $(C_SOURCES) 2>/dev/null | sed -e 's/.*: \(.*\/\).*\.c/\1\0/' | sed -e 's/.*:/build\/&/' >> .Makefile.depend endif ifneq ($(strip $(CC_SOURCES)),) @$(CXX) $(ALL_CXXFLAGS) -I. -MM $(CC_SOURCES) 2>/dev/null | sed -e 's/.*: \(.*\/\).*\.cpp/\1\0/' | sed -e 's/.*:/build\/&/' >> .Makefile.depend endif @$(RST) clean: @rm -rf $(OBJDIR) @rm -f $(NAME) $(NAME)-gl $(NAME)d lib$(NAME).so .Makefile.depend test/sizeof sizeof.h */config.h # @rm -rf ./doc/ mmpong-0.9.1/summon_clients.sh0000755000175000017500000000013511131154266015357 0ustar andreandre#!/bin/bash for i in `seq 1 1000`; do LD_LIBRARY_PATH=. ./test/perfect_bot localhost & done mmpong-0.9.1/client_gl/0000755000175000017500000000000011354734767013743 5ustar andreandremmpong-0.9.1/client_gl/macosx/0000755000175000017500000000000011354734767015235 5ustar andreandremmpong-0.9.1/client_gl/macosx/ReadMeDevLite.txt0000644000175000017500000000275511127471411020377 0ustar andreandreThis directory is for developers. This directory contains some basic essentials you will need for developing SDL based applications on OS X. The SDL-devel package contains all of this stuff plus more, so you can ignore this if you install the SDL-devel.pkg. The SDL-devel package contains Project Builder/Xcode templates, SDL documentation, and different variations of SDLmain and NIB files for SDL. To compile an SDL based application on OS X, SDLMain.m must be compiled into your program. (See the SDL FAQ). The SDL-devel.pkg includes Project Builder/Xcode templates which already do this for you. But for those who may not want to install the dev package, an SDLMain is provided here as a convenience. Be aware that there are different variations of SDLMain.m depending on what class of SDL application you make and they are intended to work with NIB files. Only one SDLMain variant is provided here and without any NIB files. You should look to the SDL-devel package for the others. We currently do not provide a SDLMain.a file, partly to call to attention that there are different variations of SDLmain. To build from the command line, your gcc line will look something like this: gcc -I/Library/Frameworks/SDL.framework/Headers MyProgram.c SDLmain.m -framework SDL -framework Cocoa An SDL/OpenGL based application might look like: gcc -I/Library/Frameworks/SDL.framework/Headers -I/System/Library/Frameworks/OpenGL.framework/Headers MyProgram.c SDLmain.m -framework SDL -framework Cocoa -framework OpenGL mmpong-0.9.1/client_gl/macosx/SDLMain.h0000644000175000017500000000046311127471411016616 0ustar andreandre/* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #import @interface SDLMain : NSObject @end mmpong-0.9.1/client_gl/macosx/SDLMain.m0000644000175000017500000002572011127471411016626 0ustar andreandre/* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #import "SDL.h" #import "SDLMain.h" #import /* for MAXPATHLEN */ #import /* For some reaon, Apple removed setAppleMenu from the headers in 10.4, but the method still is there and works. To avoid warnings, we declare it ourselves here. */ @interface NSApplication(SDL_Missing_Methods) - (void)setAppleMenu:(NSMenu *)menu; @end /* Use this flag to determine whether we use SDLMain.nib or not */ #define SDL_USE_NIB_FILE 0 /* Use this flag to determine whether we use CPS (docking) or not */ #define SDL_USE_CPS 1 #ifdef SDL_USE_CPS /* Portions of CPS.h */ typedef struct CPSProcessSerNum { UInt32 lo; UInt32 hi; } CPSProcessSerNum; extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); #endif /* SDL_USE_CPS */ static int gArgc; static char **gArgv; static BOOL gFinderLaunch; static BOOL gCalledAppMainline = FALSE; static NSString *getApplicationName(void) { NSDictionary *dict; NSString *appName = 0; /* Determine the application name */ dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); if (dict) appName = [dict objectForKey: @"CFBundleName"]; if (![appName length]) appName = [[NSProcessInfo processInfo] processName]; return appName; } #if SDL_USE_NIB_FILE /* A helper category for NSString */ @interface NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; @end #endif @interface SDLApplication : NSApplication @end @implementation SDLApplication /* Invoked from the Quit menu item */ - (void)terminate:(id)sender { /* Post a SDL_QUIT event */ SDL_Event event; event.type = SDL_QUIT; SDL_PushEvent(&event); } @end /* The main class of the application, the application's delegate */ @implementation SDLMain /* Set the working directory to the .app's parent directory */ - (void) setupWorkingDirectory:(BOOL)shouldChdir { if (shouldChdir) { char parentdir[MAXPATHLEN]; CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) { assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ } CFRelease(url); CFRelease(url2); } } #if SDL_USE_NIB_FILE /* Fix menu to contain the real app name instead of "SDL App" */ - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName { NSRange aRange; NSEnumerator *enumerator; NSMenuItem *menuItem; aRange = [[aMenu title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; enumerator = [[aMenu itemArray] objectEnumerator]; while ((menuItem = [enumerator nextObject])) { aRange = [[menuItem title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; if ([menuItem hasSubmenu]) [self fixMenu:[menuItem submenu] withAppName:appName]; } [ aMenu sizeToFit ]; } #else static void setApplicationMenu(void) { /* warning: this code is very odd */ NSMenu *appleMenu; NSMenuItem *menuItem; NSString *title; NSString *appName; appName = getApplicationName(); appleMenu = [[NSMenu alloc] initWithTitle:@""]; /* Add menu items */ title = [@"About " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Hide " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Quit " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; /* Put menu into the menubar */ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; [menuItem setSubmenu:appleMenu]; [[NSApp mainMenu] addItem:menuItem]; /* Tell the application object that this is now the application menu */ [NSApp setAppleMenu:appleMenu]; /* Finally give up our references to the objects */ [appleMenu release]; [menuItem release]; } /* Create a window menu */ static void setupWindowMenu(void) { NSMenu *windowMenu; NSMenuItem *windowMenuItem; NSMenuItem *menuItem; windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; /* "Minimize" item */ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItem:menuItem]; [menuItem release]; /* Put menu into the menubar */ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; [windowMenuItem setSubmenu:windowMenu]; [[NSApp mainMenu] addItem:windowMenuItem]; /* Tell the application object that this is now the window menu */ [NSApp setWindowsMenu:windowMenu]; /* Finally give up our references to the objects */ [windowMenu release]; [windowMenuItem release]; } /* Replacement for NSApplicationMain */ static void CustomApplicationMain (int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; SDLMain *sdlMain; /* Ensure the application object is initialised */ [SDLApplication sharedApplication]; #ifdef SDL_USE_CPS { CPSProcessSerNum PSN; /* Tell the dock about us */ if (!CPSGetCurrentProcess(&PSN)) if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) if (!CPSSetFrontProcess(&PSN)) [SDLApplication sharedApplication]; } #endif /* SDL_USE_CPS */ /* Set up the menubar */ [NSApp setMainMenu:[[NSMenu alloc] init]]; setApplicationMenu(); setupWindowMenu(); /* Create SDLMain and make it the app delegate */ sdlMain = [[SDLMain alloc] init]; [NSApp setDelegate:sdlMain]; /* Start the main event loop */ [NSApp run]; [sdlMain release]; [pool release]; } #endif /* * Catch document open requests...this lets us notice files when the app * was launched by double-clicking a document, or when a document was * dragged/dropped on the app's icon. You need to have a * CFBundleDocumentsType section in your Info.plist to get this message, * apparently. * * Files are added to gArgv, so to the app, they'll look like command line * arguments. Previously, apps launched from the finder had nothing but * an argv[0]. * * This message may be received multiple times to open several docs on launch. * * This message is ignored once the app's mainline has been called. */ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { const char *temparg; size_t arglen; char *arg; char **newargv; if (!gFinderLaunch) /* MacOS is passing command line args. */ return FALSE; if (gCalledAppMainline) /* app has started, ignore this document. */ return FALSE; temparg = [filename UTF8String]; arglen = SDL_strlen(temparg) + 1; arg = (char *) SDL_malloc(arglen); if (arg == NULL) return FALSE; newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); if (newargv == NULL) { SDL_free(arg); return FALSE; } gArgv = newargv; SDL_strlcpy(arg, temparg, arglen); gArgv[gArgc++] = arg; gArgv[gArgc] = NULL; return TRUE; } /* Called when the internal event loop has just started running */ - (void) applicationDidFinishLaunching: (NSNotification *) note { int status; /* Set the working directory to the .app's parent directory */ [self setupWorkingDirectory:gFinderLaunch]; #if SDL_USE_NIB_FILE /* Set the main menu to contain the real app name instead of "SDL App" */ [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; #endif /* Hand off to main application code */ gCalledAppMainline = TRUE; status = SDL_main (gArgc, gArgv); /* We're done, thank you for playing */ exit(status); } @end @implementation NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString { unsigned int bufferSize; unsigned int selfLen = [self length]; unsigned int aStringLen = [aString length]; unichar *buffer; NSRange localRange; NSString *result; bufferSize = selfLen + aStringLen - aRange.length; buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar)); /* Get first part into buffer */ localRange.location = 0; localRange.length = aRange.location; [self getCharacters:buffer range:localRange]; /* Get middle part into buffer */ localRange.location = 0; localRange.length = aStringLen; [aString getCharacters:(buffer+aRange.location) range:localRange]; /* Get last part into buffer */ localRange.location = aRange.location + aRange.length; localRange.length = selfLen - localRange.location; [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; /* Build output string */ result = [NSString stringWithCharacters:buffer length:bufferSize]; NSDeallocateMemoryPages(buffer, bufferSize); return result; } @end #ifdef main # undef main #endif /* Main entry point to executable - should *not* be SDL_main! */ int main (int argc, char **argv) { /* Copy the arguments into a global variable */ /* This is passed if we are launched by double-clicking */ if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { gArgv = (char **) SDL_malloc(sizeof (char *) * 2); gArgv[0] = argv[0]; gArgv[1] = NULL; gArgc = 1; gFinderLaunch = YES; } else { int i; gArgc = argc; gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); for (i = 0; i <= argc; i++) gArgv[i] = argv[i]; gFinderLaunch = NO; } #if SDL_USE_NIB_FILE [SDLApplication poseAsClass:[NSApplication class]]; NSApplicationMain (argc, argv); #else CustomApplicationMain (argc, argv); #endif return 0; } mmpong-0.9.1/client_gl/game.h0000644000175000017500000000175011131237602015003 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __GAME_H__ #define __GAME_H__ #include "lib/game.h" class Game { protected: gameplay game; uint16_t team; public: const gameplay *get_game() { return &game; }; const uint16_t get_team() { return team; }; }; #endif mmpong-0.9.1/client_gl/clientstate.h0000644000175000017500000000222011132741000016373 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __CLIENTSTATE_H__ #define __CLIENTSTATE_H__ #include #include "netgame.h" #include "renderer.h" class ClientState { public: bool signal_exiting; bool sound; bool sexy; bool full; std::string server, port; NetGame *game; Renderer *renderer; ClientState() { signal_exiting = 0; sound = 1; sexy = 0; full = 0; server = "localhost"; port = "1212"; game = NULL; } }; #endif mmpong-0.9.1/client_gl/glhelper.h0000644000175000017500000000206511130162146015673 0ustar andreandre/* Copyright (C) 2008 Jan Friederich This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __GLHELPER__ #define __GLHELPER__ #include "SDL.h" #include "SDL_opengl.h" #include "SDL_image.h" #include #include void create_box(GLuint *); void create_cylinder(GLuint *, const GLuint); void create_ball(GLuint *, const GLuint, const GLuint); int load_png(GLuint *, const char *); #endif mmpong-0.9.1/client_gl/client.cpp0000644000175000017500000002511211132741000015672 0ustar andreandre/* Copyright (C) 2008 Steffen Basting, André Gaul, Jan Friederich This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifdef WIN32 # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0501 //assume windows xp or above # endif # include # include #endif #ifdef __APPLE__ #include #endif #include #include #include #include #include #include "clientstate.h" #include "config.h" #include "gui.h" #include "netgame.h" #include "renderer.h" #include "resource.h" using namespace std; #define DEFAULT_WINDOW_TITLE "mmpong-gl" #define DEFAULT_WINDOW_WIDTH 800 #define DEFAULT_WINDOW_HEIGHT 600 #define DEFAULT_FRAMERATE 30 #define SIGN(sgn_u) ((sgn_u < 0) ? -1 : 1) // Audio stuff. TODO: do it nicely ;-) static Mix_Chunk *mx_pong1, *mx_pong2, *mx_score; // signal handler for POSIX signals #ifndef WIN32 ClientState *globalstate; void sigterm_handler(int signal) { if (signal == SIGPIPE) printf("Caught SIGPIPE.\n"); if (signal == SIGURG) printf("Caught SIGURG.\n"); if (signal == SIGTERM) globalstate->signal_exiting= 1; } #endif // command line options parser static int parse_options(int argc, char **argv, ClientState *state) { const char *short_options= "szh"; const struct option long_options[]= { { "sexy", no_argument, NULL, 's' }, { "nosound", no_argument, NULL, 'z' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, '\0' } }; const char *options_desc[]= { "Enable graphics effects (fast hardware recommended)", "Refrain from playing annoying sounds (your co-workers will appreciate it)", "Display this message and exit", NULL }; extern int optind; int opt, helpscreen= 0; while ((opt = getopt_long(argc, argv, short_options, long_options, NULL))) { if (opt == (-1)) break; switch(opt) { case 's': state->sexy= true; break; case 'z': state->sound= false; break; case 'h': helpscreen= true; break; case '?': exit(1); break; // :-) default: exit(1); break; // :-) } } if (/*optind >= argc ||*/ argc - optind > 2 || helpscreen) { printf("mmpong-gl client %d.%02d (r%s.c%d.l%d)\nReleased under the terms of the GPL (http://www.gnu.org/licenses/gpl.html).\n", VER_MAJ, VER_MIN, VER_SVN, (int)sizeof(struct gameplay), gameplay_getversion()); printf("Usage: %s", argv[0]); for (int i = 0; long_options[i].name; ++i) printf(" [--%s]", long_options[i].name); printf(" [server [port]]\n"); for (int i = 0; options_desc[i] && long_options[i].name; ++i) { printf("\t-%c, --%s:\t%s\n", long_options[i].val, long_options[i].name, options_desc[i]); } exit(1); } return optind; } int init_sound() { int audio_rate = 22050; Uint16 audio_format = AUDIO_S16; int audio_channels = 2; int audio_buffers = 512; if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) { fprintf(stderr, "SDL_mixer: Unable to use Mix_OpenAudio!\n"); //return PONG_FAILURE; } mx_pong1 = Mix_LoadWAV(RESOURCE("sounds/pong_1.wav")); mx_pong2 = Mix_LoadWAV(RESOURCE("sounds/pong_2.wav")); mx_score = Mix_LoadWAV(RESOURCE("sounds/score.wav")); return PONG_SUCCESS; } int main(int argc, char *argv[]) { ClientState state; # ifndef WIN32 globalstate = &state; # endif errno=0; int x, y; uint8_t *key_state; uint32_t mouse_state, score[2]; float dir_old = 0.0; #ifdef __APPLE__ char respath[1024]; CFBundleRef mainBundle = CFBundleGetMainBundle(); assert(mainBundle); CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle); assert(mainBundleURL); CFStringRef cfStringRef = CFURLCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle); assert(cfStringRef); CFStringGetCString(cfStringRef, respath, 1024, kCFStringEncodingASCII); CFRelease(mainBundleURL); CFRelease(cfStringRef); string respathpp(respath); respathpp += "/Contents/Resources/"; RESOURCE(respathpp.c_str()); #endif int argopt = parse_options(argc, argv, &state); #ifndef WIN32 struct sigaction termact; termact.sa_handler= sigterm_handler; termact.sa_flags= SA_RESETHAND; sigemptyset(&termact.sa_mask); if ( (sigaction(SIGTERM, &termact, NULL)) || (termact.sa_flags= SA_RESTART, sigaction(SIGPIPE, &termact, NULL)) ) { fprintf(stderr, "Error putting signal handlers in place.\n"); exit(-1); } #else //winsock init WSADATA wsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &wsaData); if (wsaresult != 0) { fprintf(stderr, "Error initializing Windows Sockets (code==%d)\n", wsaresult); exit(-1); } #endif char *portname= (char *)"1212"; if (argc - argopt > 1) portname= argv[argopt+1]; if (argc - argopt > 0) { state.server = argv[argopt]; state.port = portname; } Renderer renderer(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, state.full, state.sexy, RESOURCE(""), DEFAULT_WINDOW_TITLE); state.renderer = &renderer; GUI gui(RESOURCE(""), renderer.get_width(), renderer.get_height(), true, &state); if (argc - argopt > 0) { // try to connect directly without showing a non-nerdy GUI gui.set_visible(false); try { state.game = new NetGame(state.server, state.port, 0); } catch (runtime_error &err) { cerr << err.what() << endl; gui.show_dialog("Error", err.what()); gui.set_visible(true); delete state.game; state.game = NULL; } } float pos=0.5; int holdgrab= 0; // Save scores if (state.game) for (int idx=0;idx<=1;idx++) score[idx] = state.game->get_game()->pad_attr[idx].score; init_sound(); const struct timeval time_wait = { 0, 1000 * 1000 / DEFAULT_FRAMERATE }; struct timeval time_start, time_diff; memcpy(&time_diff, &time_wait, sizeof(struct timeval)); while(!state.signal_exiting) { // check for time 'overflow' if (time_wait.tv_sec < time_diff.tv_sec || (time_wait.tv_sec == time_diff.tv_sec && time_wait.tv_usec < time_diff.tv_usec) ) // no more time left :( { time_diff.tv_sec = 0; time_diff.tv_usec = 0; } else timeval_combine(-1, &time_diff, 1, &time_wait); if (state.game) { // update game (if available) try { state.game->update(&time_diff); } catch (runtime_error &err) { cerr << err.what() << endl; gui.show_dialog("Error", err.what()); gui.set_visible(true); delete state.game; state.game = NULL; } } else #ifdef WIN32 Sleep( (time_wait.tv_sec * 1000) + (time_wait.tv_usec / 1000) ); #else usleep( ((long)time_wait.tv_sec) * 1000 * 1000 + time_wait.tv_usec ); #endif gettimeofday(&time_start, NULL); // get events SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) state.signal_exiting = 1; // handle 'important' events here if (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE) gui.set_visible(!gui.get_visible()); if (event.type == SDL_VIDEORESIZE) { gui.resize_init(); renderer.set_winsize(event.resize.w, event.resize.h); gui.resize_finish(event.resize.w, event.resize.h); } if (gui.get_visible()) { // gui handles events gui.handle_event(event); } else { if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE) { holdgrab = !holdgrab; SDL_WM_GrabInput( holdgrab ? SDL_GRAB_ON : SDL_GRAB_OFF); SDL_WM_SetCaption( (holdgrab)? DEFAULT_WINDOW_TITLE" (Press space bar to release pointer)" : DEFAULT_WINDOW_TITLE, NULL); } if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_q) state.signal_exiting = 1; if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_RETURN) gui.set_full(!renderer.get_full()); } } if (state.game) { // flush send buffer try { state.game->sendpos_flush(); // process keyboard and mouse for position if (!gui.get_visible()) { // get position bool newpos=false; key_state = SDL_GetKeyState(NULL); mouse_state = SDL_GetRelativeMouseState(&x, &y); if (y != 0) { pos -= 2.0f*((float)(y)/600); newpos = true; } if (key_state[SDLK_UP]) { pos += 0.05f;//state.game->get_game()->pad[team].size / 2; newpos = true; } if (key_state[SDLK_DOWN]) { pos -= 0.05f;//state.game->get_game()->pad[team].size / 2; newpos = true; } if (pos > 1.0f - state.game->get_game()->pad[state.game->get_team()].size/2.0f) pos = 1.0f - state.game->get_game()->pad[state.game->get_team()].size/2.0f; if (pos < state.game->get_game()->pad[state.game->get_team()].size/2.0f) pos = state.game->get_game()->pad[state.game->get_team()].size/2.0f; // send position if (newpos) state.game->sendpos(pos); } } catch (runtime_error &err) { cerr << err.what() << endl; gui.show_dialog("Error", err.what()); gui.set_visible(true); delete state.game; state.game = NULL; } // render game (if available) // note: do not combine with the code above! (game maybe null now!) try { renderer.render(*state.game, pos); } catch (runtime_error &err) { // no way to go out smoothly if renderer is broken cerr << err.what() << endl; exit(-1); } // play score-sound for (int idx=0; state.sound && idx<=1;idx++) if (score[idx] != state.game->get_game()->pad_attr[idx].score) { Mix_PlayChannel(-1, mx_score, 0); score[idx] = state.game->get_game()->pad_attr[idx].score; break; } // play bounce-sound // Play annoying sounds when the ball bounces off the paddle if (state.sound && dir_old != 0.0 && (SIGN(dir_old) != SIGN(state.game->get_game()->ball.dir[0]))) { if (dir_old < 0.0) Mix_PlayChannel(-1, mx_pong1, 0); else Mix_PlayChannel(-1, mx_pong2, 0); } dir_old = state.game->get_game()->ball.dir[0]; } else { // just make a blank screen with field texture // if no game is running renderer.render(); } // this is kinda self-explanatory ;) gui.render(); // show it SDL_GL_SwapBuffers(); gettimeofday(&time_diff, NULL); timeval_combine(1, &time_diff, -1, &time_start); } Mix_CloseAudio(); // Close audio stuff #ifdef WIN32 WSACleanup(); #endif return 0; } mmpong-0.9.1/client_gl/config.h.in0000644000175000017500000000150411131661655015752 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __VERSION_H__ #define __VERSION_H__ #define VER_MAJ 0 #define VER_MIN 9 #define VER_SVN "${SVN_REV}" #define RESPATH "${CLIENT_GL_RES_PATH}" #endif mmpong-0.9.1/client_gl/renderer.cpp0000644000175000017500000003534611132462032016241 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include "resource.h" #include "renderer.h" #include "glhelper.h" #define BALL_RADIUS 0.03f #define PADDLE_RADIUS 0.05f #define BOUNDARY_RADIUS 0.05f #define BLUR_DECAY 0.15f #define DIGIT_WIDTH (40.f/512.f) #define DIGIT_RATIO (40.f/64.f) #define SCORE_WIDTH (192.f/512.f) #define SCORE_RATIO (192.f/64.f) #define SCORE_XOFFSET (0.0f) #define SCORE_FONT_SIZE 0.07f #define PLAYERS_WIDTH (264.f/512.f) #define PLAYERS_RATIO (264.f/64.f) #define PLAYERS_XOFFSET (192.f/512.f) #define PLAYERS_FONT_SIZE (0.04f) using namespace std; Renderer::Renderer(int _win_w, int _win_h, bool _full, bool _sexy, const string &respath, const string &title){ win_w = _win_w; win_h = _win_h; sexy = _sexy; full = _full; if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) throw runtime_error(string("Renderer: Unable to initialize SDL: ") + SDL_GetError()); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 16); if (sexy) { SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 4); SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 4); SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 4); SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 4); } // use SDL_GetVideoInfo call before SDL_SetVideoMode to get screen resolution const SDL_VideoInfo *info = SDL_GetVideoInfo(); if (!info) throw runtime_error("Renderer: Unable to get video info"); full_w = info->current_w; full_h = info->current_h; video_flags = SDL_OPENGL | (info->hw_available ? SDL_HWSURFACE : SDL_SWSURFACE); SDL_WM_SetCaption(title.c_str(), NULL); SDL_Surface *icon = IMG_Load(RESOURCE("icon/mmpong.png")); if (icon) SDL_WM_SetIcon(icon, NULL); else cerr << "could not load bitmap: "<< RESOURCE("icon/mmpong.png") << endl; SDL_ShowCursor(0); resize(); # if !(defined(__APPLE__) || defined(WIN32)) init_graphics(); # endif } Renderer::~Renderer() { glDeleteTextures(1, &score_texture); glDeleteTextures(1, &field_texture); glDeleteLists(box_list, 1); glDeleteLists(cylinder_list, 1); glDeleteLists(ball_list, 1); SDL_Quit(); } void Renderer::resize() { int width=get_width(), height=get_height(); if ((surface = SDL_SetVideoMode(width, height, 0, video_flags | (full ? SDL_FULLSCREEN : SDL_RESIZABLE))) == NULL) throw runtime_error(std::string("Renderer: Unable to create OpenGL screen: ") + SDL_GetError()); /* This is a hack for Windows and SDL 1.2: In Windows, the OpenGL state seems to get lost every time we call SDL_SetVideoMode(). That is, we have to reset the state which unfortunately include loading the textures from files etc. Hopefully this will be fixed in SDL 1.3. Note that calling SDL_SetVideoMode() is necessary for the graphics to be displayed properly and the mouse events to provide correct data. */ #if (defined(__APPLE__) || defined(WIN32)) init_graphics(); #endif if (width < height) { glViewport(0, (height-width)/2, width, width); height = width; } else glViewport(0, 0, width, height); if (height <= 0) height = 1; glMatrixMode(GL_PROJECTION); glLoadIdentity(); // z-planes need some tuning gluPerspective(45.0f, ((float)width)/height, 0.001f, 10.0f); glMatrixMode(GL_MODELVIEW); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT); field_width = 0.9f*((float)width)/height; } void Renderer::init_graphics() { glShadeModel(GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); //glEnable(GL_NORMALIZE); //glEnable(GL_DEPTH_TEST); //glDepthFunc(GL_LEQUAL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLfloat ambient[] = {0.2f, 0.2f, 0.2f, 0.5f}; GLfloat diffuse[] = {0.8f, 0.8f, 0.8f, 1.0f}; GLfloat position[] = {0.0f, 1.0f, 1.0f, 0.0f}; glLightfv(GL_LIGHT1, GL_AMBIENT, ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT1, GL_POSITION, position); glEnable(GL_LIGHT1); create_box(&box_list); create_cylinder(&cylinder_list, 16); create_ball(&ball_list, 4, 16); if (load_png(&score_texture, RESOURCE("images/score.png")) != 0) cerr << "Renderer: Unable to load texture: " << RESOURCE("images/score.png") << endl; if (load_png(&field_texture, RESOURCE("images/field.png")) != 0) cerr << "Renderer: Unable to load texture: " << RESOURCE("images/field.png") << endl; } void Renderer::render(Game &game, float pos) { GLfloat color[] = {0.0f, 0.0f, 0.0f, 0.0f}; glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.5f*field_width, 0.5f, 1.5f, 0.5f*field_width, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f); glPushMatrix(); glRotatef(-22.5f, 1.0f, 0.0f, 0.0f); if (sexy) { glAccum(GL_MULT, 1.0f-BLUR_DECAY); color[0] = 0.7f, color[1] = 0.7f, color[2] = 0.0f, color[3] = 1.0f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(game.get_game()->ball.pos[0]*field_width, game.get_game()->ball.pos[1], 0.0f); glScalef(2.0f*BALL_RADIUS, 2.0f*BALL_RADIUS, 2.0f*BALL_RADIUS); glCallList(ball_list); glPopMatrix(); glAccum(GL_ACCUM, 1.0f); glAccum(GL_RETURN, 1.0f); } glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, field_texture); color[0] = 0.1f, color[1] = 0.1f, color[2] = 0.1f, color[3] = 0.3f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glScalef(field_width, 1.0f, 1.0f); glTranslatef(0.5f, 0.5f, 0.0f); glRotatef(45.0f, 0.0f, 0.0f, 1.0f); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.6f, -0.2f, 0.0f); // bottom left glTexCoord2f(1.0f, 0.0f); glVertex3f(0.6f, -0.2f, 0.0f); // bottom right glTexCoord2f(1.0f, 1.0f); glVertex3f(0.6f, 0.2f, 0.0f); // top right glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.6f, 0.2f, 0.0f); // top left glEnd(); glPopMatrix(); glDisable(GL_TEXTURE_2D); color[0] = 0.4f, color[1] = 0.4f, color[2] = 0.4f, color[3] = 0.2f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(0.5f*field_width, 1.0f+0.5f*BOUNDARY_RADIUS+BALL_RADIUS, 0.0f); glScalef(field_width+2.0f*BALL_RADIUS+2.0f*PADDLE_RADIUS, BOUNDARY_RADIUS, BOUNDARY_RADIUS); glCallList(box_list); glPopMatrix(); glPushMatrix(); glTranslatef(0.5f*field_width, -0.5f*BOUNDARY_RADIUS-BALL_RADIUS, 0.0f); glScalef(field_width+2.0f*BALL_RADIUS+2.0f*PADDLE_RADIUS, BOUNDARY_RADIUS, BOUNDARY_RADIUS); glCallList(box_list); glPopMatrix(); draw_paddle(pos, game.get_game()->pad[game.get_team()].size, game.get_team(), 1, game.get_game()->pad_attr[game.get_team()].profile); for (unsigned int i = 0; i < 2; ++i) { draw_paddle(game.get_game()->pad[i].mean, game.get_game()->pad[i].size, i, 0, game.get_game()->pad_attr[i].profile); draw_variance(game.get_game()->pad[i].mean, game.get_game()->pad[i].var, i); } // draw opaque ball in any case color[0] = 0.7f, color[1] = 0.7f, color[2] = 0.0f, color[3] = 1.0f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); //color[0] = 1.0f, color[1] = 1.0f, color[2] = 1.0f, color[3] = 1.0f; //glMaterialfv(GL_FRONT, GL_SPECULAR, color); //glMaterialf(GL_FRONT, GL_SHININESS, 30.0f); glPushMatrix(); glTranslatef(game.get_game()->ball.pos[0]*field_width, game.get_game()->ball.pos[1], 0.0f); glScalef(2.0f*BALL_RADIUS, 2.0f*BALL_RADIUS, 2.0f*BALL_RADIUS); glCallList(ball_list); glPopMatrix(); glPopMatrix(); for (unsigned int i = 0; i < 2; ++i) { draw_score(i, game.get_game()->pad_attr[i].score); draw_players(i, game.get_game()->pad_attr[i].peers); } } void Renderer::draw_paddle(float mean, float size, uint16_t team, uint16_t mode, uint16_t profile) { GLfloat color[] = {team ? 1.0f : 0.0f, 0.0f, team ? 0.0f : 1.0, mode ? 1.0f : 0.5f}; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); /*if (mode) { glTranslatef(team ? field_width+0.5f*PADDLE_RADIUS+BALL_RADIUS : -0.5f*PADDLE_RADIUS-BALL_RADIUS, mean, 0.0f); glScalef(PADDLE_RADIUS, size, PADDLE_RADIUS); } else { glTranslatef(team ? field_width+0.25f*PADDLE_RADIUS+BALL_RADIUS : -0.25f*PADDLE_RADIUS-BALL_RADIUS, mean, 0.0f); glScalef(0.5f*PADDLE_RADIUS, size, PADDLE_RADIUS); }*/ glTranslatef(team ? field_width+0.5f*PADDLE_RADIUS+BALL_RADIUS : -0.5f*PADDLE_RADIUS-BALL_RADIUS, mean, 0.0f); glScalef(PADDLE_RADIUS, size, PADDLE_RADIUS); if (profile) glCallList(cylinder_list); else glCallList(box_list); glPopMatrix(); }; void Renderer::draw_players(uint16_t team, uint16_t number) { GLfloat color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; //glPushAttrib(GL_TEXTURE_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, score_texture); //glEnable(GL_BLEND); //glDepthMask(GL_FALSE); //glBlendFunc(GL_SRC_ALPHA, GL_ONE); //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); if (!team) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(0.5f*field_width, 1.04f, 0.0f); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(PLAYERS_XOFFSET, 0.0f); glVertex3f(-0.5f*PLAYERS_RATIO*PLAYERS_FONT_SIZE, 0.0f, 0.0f); // bottom left glTexCoord2f(PLAYERS_XOFFSET+PLAYERS_WIDTH, 0.0f); glVertex3f(0.5f*PLAYERS_RATIO*PLAYERS_FONT_SIZE, 0.0f, 0.0f); // bottom right glTexCoord2f(PLAYERS_XOFFSET+PLAYERS_WIDTH, 0.5f); glVertex3f(0.5f*PLAYERS_RATIO*PLAYERS_FONT_SIZE, PLAYERS_FONT_SIZE, 0.0f); // top right glTexCoord2f(PLAYERS_XOFFSET, 0.5f); glVertex3f(-0.5f*PLAYERS_RATIO*PLAYERS_FONT_SIZE, PLAYERS_FONT_SIZE, 0.0f); // top left glEnd(); glPopMatrix(); } uint16_t n = 1, m = number/10; while (m > 0) ++n, m/=10; color[0] = team ? 1.0f : 0.0f, color[1] = 0.0f, color[2] = team ? 0.0f : 1.0f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(team ? 0.8f*field_width : 0.2f*field_width, 1.04f, 0.0f); glTranslatef(team ? -1.f*DIGIT_RATIO*PLAYERS_FONT_SIZE : (n-1)*DIGIT_RATIO*PLAYERS_FONT_SIZE, 0.0f, 0.0f); draw_digit(number%10, PLAYERS_FONT_SIZE); number /= 10; while (number > 0) { glTranslatef(-DIGIT_RATIO*PLAYERS_FONT_SIZE, 0.0f, 0.0f); draw_digit(number%10, PLAYERS_FONT_SIZE); number /= 10; } glPopMatrix(); glDisable(GL_TEXTURE_2D); //glPopAttrib(GL_TEXTURE_BIT); }; void Renderer::draw_score(uint16_t team, uint16_t score) { GLfloat color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; //glPushAttrib(GL_TEXTURE_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, score_texture); //glEnable(GL_BLEND); //glDepthMask(GL_FALSE); //glBlendFunc(GL_SRC_ALPHA, GL_ONE); //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); if (!team) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(0.5f*field_width, 0.94f, 0.0f); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(SCORE_XOFFSET, 0.0f); glVertex3f(-0.5f*SCORE_RATIO*SCORE_FONT_SIZE, 0.0f, 0.0f); // bottom left glTexCoord2f(SCORE_XOFFSET+SCORE_WIDTH, 0.0f); glVertex3f(0.5f*SCORE_RATIO*SCORE_FONT_SIZE, 0.0f, 0.0f); // bottom right glTexCoord2f(SCORE_XOFFSET+SCORE_WIDTH, 0.5f); glVertex3f(0.5f*SCORE_RATIO*SCORE_FONT_SIZE, SCORE_FONT_SIZE, 0.0f); // top right glTexCoord2f(SCORE_XOFFSET, 0.5f); glVertex3f(-0.5f*SCORE_RATIO*SCORE_FONT_SIZE, SCORE_FONT_SIZE, 0.0f); // top left glEnd(); glPopMatrix(); } uint16_t n = 1, m = score/10; while (m > 0) ++n, m/=10; color[0] = team ? 1.0f : 0.0f, color[1] = 0.0f, color[2] = team ? 0.0f : 1.0f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(team ? 0.95f*field_width : 0.05f*field_width, 0.94f, 0.0f); glTranslatef(team ? -1.f*DIGIT_RATIO*SCORE_FONT_SIZE : (n-1)*DIGIT_RATIO*SCORE_FONT_SIZE, 0.0f, 0.0f); draw_digit(score%10, SCORE_FONT_SIZE); score /= 10; while (score > 0) { glTranslatef(-DIGIT_RATIO*SCORE_FONT_SIZE, 0.0f, 0.0f); draw_digit(score%10, SCORE_FONT_SIZE); score /= 10; } glPopMatrix(); glDisable(GL_TEXTURE_2D); //glPopAttrib(GL_TEXTURE_BIT); }; void Renderer::draw_variance(float mean, float var, uint16_t team) { GLfloat color[] = {team ? 1.0f : 0.0f, 0.0f, team ? 0.0f : 1.0, 0.5f}; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glTranslatef(team ? field_width+2.5f*PADDLE_RADIUS+BALL_RADIUS : -2.5f*PADDLE_RADIUS-BALL_RADIUS, 0.5f, 0.0f); glScalef(PADDLE_RADIUS, 2.0f*sqrt(var), PADDLE_RADIUS); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, 0.0f); glVertex3f(0.5f, -0.5f, 0.0f); glVertex3f(0.5f, 0.5f, 0.0f); glVertex3f(-0.5f, 0.5f, 0.0f); glEnd(); glPopMatrix(); }; void Renderer::draw_digit(uint16_t digit, float size) { GLfloat u = digit * DIGIT_WIDTH; glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(u, 0.5f); glVertex3f(0.0f, 0.0f, 0.0f); // bottom left glTexCoord2f(u+DIGIT_WIDTH, 0.5f); glVertex3f(DIGIT_RATIO*size, 0.0f, 0.0f); // bottom right glTexCoord2f(u+DIGIT_WIDTH, 1.0f); glVertex3f(DIGIT_RATIO*size, size, 0.0f); // top right glTexCoord2f(u, 1.0f); glVertex3f(0.0f, size, 0.0f); // top left glEnd(); } void Renderer::draw_field() { GLfloat color[] = {0.0f, 0.0f, 0.0f, 0.0f}; glPushMatrix(); glRotatef(-22.5f, 1.0f, 0.0f, 0.0f); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, field_texture); color[0] = 0.1f, color[1] = 0.1f, color[2] = 0.1f, color[3] = 0.3f; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color); glPushMatrix(); glScalef(field_width, 1.0f, 1.0f); glTranslatef(0.5f, 0.5f, 0.0f); glRotatef(45.0f, 0.0f, 0.0f, 1.0f); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.6f, -0.2f, 0.0f); // bottom left glTexCoord2f(1.0f, 0.0f); glVertex3f(0.6f, -0.2f, 0.0f); // bottom right glTexCoord2f(1.0f, 1.0f); glVertex3f(0.6f, 0.2f, 0.0f); // top right glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.6f, 0.2f, 0.0f); // top left glEnd(); glPopMatrix(); glDisable(GL_TEXTURE_2D); glPopMatrix(); } void Renderer::render() { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); gluLookAt(0.5f*field_width, 0.5f, 1.5f, 0.5f*field_width, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f); draw_field(); } mmpong-0.9.1/client_gl/gui.h0000644000175000017500000000456411132665652014677 0ustar andreandre/* Copyright (C) 2008 Jan Friederich, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __GUI_H__ #define __GUI_H__ #include #include #include #include "clientstate.h" class dummyLogger : public CEGUI::Logger { public: dummyLogger() : CEGUI::Logger() {}; ~dummyLogger() {}; void logEvent(const CEGUI::String& message, CEGUI::LoggingLevel level = CEGUI::Standard) {}; void setLogFilename(const CEGUI::String& filename, bool append = false) {}; }; class GUI { private: bool visible; ClientState *state; CEGUI::OpenGLRenderer *renderer; dummyLogger *log; CEGUI::System *sys; double last_time; void handle_mouse_down(Uint8 button); void handle_mouse_up(Uint8 button); // gui elements CEGUI::Window *win_config; CEGUI::Editbox *edit_server, *edit_port; CEGUI::Checkbox *check_sexy, *check_sound, *check_full; // gui actions bool on_sexy(const CEGUI::EventArgs &e); bool on_sound(const CEGUI::EventArgs &e); bool on_full(const CEGUI::EventArgs &e); bool on_quit(const CEGUI::EventArgs &e); bool on_cancel(const CEGUI::EventArgs &e); bool on_connect(const CEGUI::EventArgs &e); // dialog elements CEGUI::Window *win_dialog; CEGUI::Window *stext_dialog; // dialog actions bool on_dialog_ok(const CEGUI::EventArgs &e); public: GUI(std::string resbase, int w, int h, bool _visible, ClientState *_state); void resize_init(); void resize_finish(int w, int h); void set_full(bool _full); void set_visible(bool _visible) { visible = _visible; }; bool get_visible() { return visible; }; int handle_event(SDL_Event &e); void render(); void show_dialog(const std::string &title, const std::string &message); }; #endif mmpong-0.9.1/client_gl/resource.h0000644000175000017500000000247011132501255015720 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __RESOURCE_H__ #define __RESOURCE_H__ #include "config.h" #if defined(__APPLE__) // Mac OS X: resources are located in app bundle // (create path from argv[0]) # define RESOURCE_MAXLEN 256 const char * get_resource(const char *name); # undef RESOURCE_MAXLEN # define RESOURCE(res) get_resource(res) #elif defined(WIN32) // Windows: resources are located in same dir as EXE # define RESOURCE(res) res #else // UNIX: resource dir is defined by build system flag RESPATH # define RESOURCE(res) (RESPATH res) #endif #endif mmpong-0.9.1/client_gl/client.h0000644000175000017500000000131511121720206015341 0ustar andreandre/* Copyright (C) 2008 Steffen Basting This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #define BALL_STRING \ "**" \ "*." mmpong-0.9.1/client_gl/CMakeLists.txt0000644000175000017500000001162711132703607016471 0ustar andreandreSET( CLIENT_GL TRUE) SET( CLIENT_GL_SOURCES client.cpp game.cpp glhelper.cpp gui.cpp netgame.cpp renderer.cpp resource.cpp ) SET( SVN_REV "${MMPONG_WC_REVISION}" ) FIND_PACKAGE(SDL) IF(SDL_FOUND) MESSAGE(STATUS "found SDL!") ELSE(SDL_FOUND) SET(CLIENT_GL FALSE) ENDIF(SDL_FOUND) FIND_PACKAGE(SDL_image) IF(SDLIMAGE_FOUND) MESSAGE(STATUS "found SDLIMAGE!") ELSE(SDLIMAGE_FOUND) SET(CLIENT_GL FALSE) ENDIF(SDLIMAGE_FOUND) FIND_PACKAGE(SDL_mixer) IF(SDLMIXER_FOUND) MESSAGE(STATUS "found SDLMIXER!") ELSE(SDLMIXER_FOUND) SET(CLIENT_GL FALSE) ENDIF(SDLMIXER_FOUND) FIND_PACKAGE(OpenGL) IF(OPENGL_FOUND) MESSAGE(STATUS "found OpenGL!") ELSE(OPENGL_FOUND) SET(CLIENT_GL FALSE) ENDIF(OPENGL_FOUND) FIND_PACKAGE(PkgConfig) IF(PKG_CONFIG_FOUND) MESSAGE(STATUS "found pkg-config!") PKG_SEARCH_MODULE(CEGUI CEGUI-OPENGL) IF(CEGUI_FOUND) MESSAGE(STATUS "found CEGUI! ${CEGUI_LIBRARIES} ${CEGUI_INCLUDE_DIRS}") ENDIF(CEGUI_FOUND) ENDIF(PKG_CONFIG_FOUND) IF(NOT CEGUI_FOUND) SET(CEGUI_LIBRARIES "NOTFOUND" CACHE STRING "Path to library") SET(CEGUI_LINK_DIRS "" CACHE STRING "Path to libraries") SET(CEGUI_INCLUDE_DIRS "NOTFOUND" CACHE STRING "Path to includes") SET(CLIENT_GL FALSE CACHE BOOL "manually set flag") ENDIF(NOT CEGUI_FOUND) IF(CLIENT_GL) MESSAGE(STATUS "activating mmpong-gl.") LINK_DIRECTORIES(${CEGUI_LINK_DIRS}) IF(APPLE) SET(CLIENT_GL_SOURCES ${CLIENT_GL_SOURCES} macosx/SDLMain.m) SET(MACOSX_BUNDLE_ICON_FILE mmpong.icns) ADD_EXECUTABLE( mmpong-gl MACOSX_BUNDLE ${CLIENT_GL_SOURCES}) SET (DMG_FILE "${CMAKE_BINARY_DIR}/mmpong.dmg") #populate bundle ADD_CUSTOM_COMMAND(TARGET mmpong-gl POST_BUILD COMMAND mkdir ARGS -p ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Resources/images ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Resources/icon ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Frameworks COMMAND cp ARGS ${CMAKE_SOURCE_DIR}/resources/icon/mmpong.png ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Resources/icon/ COMMAND cp ARGS ${CMAKE_SOURCE_DIR}/resources/icon/mmpong.png ${CMAKE_SOURCE_DIR}/resources/images/*.png ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Resources/images/ COMMAND cp ARGS -R ${CMAKE_BINARY_DIR}/mmpong.framework /Library/Frameworks/SDL.framework /Library/Frameworks/SDL_image.framework /Library/Frameworks/SDL_mixer.framework ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Frameworks/ COMMAND cp ARGS -R ${CMAKE_SOURCE_DIR}/resources/icon/mmpong.icns ${CMAKE_SOURCE_DIR}/resources/sounds ${CMAKE_SOURCE_DIR}/resources/CEGUI /Library/Frameworks/CEGUI.framework /Library/Frameworks/CEGUIOpenGLRenderer.framework ${CMAKE_BINARY_DIR}/mmpong-gl.app/Contents/Resources/ COMMAND find ARGS ${CMAKE_BINARY_DIR}/mmpong-gl.app -name .svn | xargs rm -r COMMAND hdiutil ARGS create -srcfolder ${CMAKE_BINARY_DIR}/mmpong-gl.app ${DMG_FILE} -format UDBZ -volname mmpong -ov ) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/mmpong-gl.app ) ELSEIF(WIN32) IF(MINGW) ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mmpong_rc.o COMMAND windres.exe -i resources/win32/mmpong.rc -o ${CMAKE_CURRENT_BINARY_DIR}/mmpong_rc.o WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) SET(CLIENT_GL_SOURCES ${CLIENT_GL_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/mmpong_rc.o) ENDIF(MINGW) ADD_EXECUTABLE( mmpong-gl WIN32 ${CLIENT_GL_SOURCES}) ELSE(APPLE) ADD_EXECUTABLE( mmpong-gl ${CLIENT_GL_SOURCES}) SET( CLIENT_GL_RES_PATH "${CMAKE_INSTALL_PREFIX}/share/games/mmpong-gl/" ) ENDIF(APPLE) SET( CONFIGURE_DIR "${CMAKE_CURRENT_BINARY_DIR}") CONFIGURE_FILE ( config.h.in ${CONFIGURE_DIR}/config.h ) INCLUDE_DIRECTORIES( mmpong-gl ${INC_DIR_LIB} ${SDL_INCLUDE_DIR} ${SDLIMAGE_INCLUDE_DIR} ${SDLMIXER_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} ${CEGUI_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CONFIGURE_DIR}) TARGET_LINK_LIBRARIES( mmpong-gl libmmpong ${CEGUI_LIBRARIES} ${SDL_LIBRARY} ${SDLIMAGE_LIBRARY} ${SDLMIXER_LIBRARY} ${OPENGL_LIBRARIES}) IF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(TARGETS mmpong-gl RUNTIME DESTINATION games BUNDLE DESTINATION games ) ELSEIF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(TARGETS mmpong-gl RUNTIME DESTINATION games ) ENDIF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/resources/CEGUI ${PROJECT_SOURCE_DIR}/resources/icon ${PROJECT_SOURCE_DIR}/resources/images ${PROJECT_SOURCE_DIR}/resources/sounds DESTINATION share/games/mmpong-gl PATTERN ".svn" EXCLUDE PATTERN "mmpong.ico" EXCLUDE PATTERN "mmpong.icns" EXCLUDE ) INSTALL(DIRECTORY ${PROJECT_SOURCE_DIR}/resources/applications ${PROJECT_SOURCE_DIR}/resources/icons DESTINATION share PATTERN ".svn" EXCLUDE ) ELSE(CLIENT_GL) MESSAGE(STATUS "disabling mmpong-gl because of missing dependencies.") ENDIF(CLIENT_GL) mmpong-0.9.1/client_gl/resource.cpp0000644000175000017500000000271511132501327016255 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include "resource.h" #if defined(__APPLE__) // Mac OS X: resources are located in app bundle // (create path from argv[0]) # define RESOURCE_MAXLEN 256 const char * get_resource(const char *name) { static char * respath = NULL; static int respathlen; if (!name) return NULL; if (!respath && name) { respath=(char*)malloc(sizeof(char)*(strlen(name) + RESOURCE_MAXLEN)); if (!respath) return NULL; else { respathlen=strlen(name); strncpy(respath, name, respathlen+1); return respath; } } strncpy(respath+respathlen, name, RESOURCE_MAXLEN); respath[respathlen+RESOURCE_MAXLEN-1]='\0'; return respath; } # undef RESOURCE_MAXLEN #endif mmpong-0.9.1/client_gl/game.cpp0000644000175000017500000000146011131146334015335 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include "game.h" mmpong-0.9.1/client_gl/netgame.cpp0000644000175000017500000002133611132732774016062 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifdef WIN32 # ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0501 //assume windows xp or above # endif # include # include #else # include # include # include # include # include # include #endif #include #include #include #include #include #include "netgame.h" using namespace std; #ifdef WIN32 # define CLOSE closesocket #else # define CLOSE close #endif void timeval_combine(const int lfact, struct timeval *lval, const int rfact, const struct timeval *rval) { // combine linearly lval->tv_sec= lval->tv_sec * lfact + rval->tv_sec * rfact; lval->tv_usec= lval->tv_usec * lfact + rval->tv_usec * rfact; // normalize if (lval->tv_usec < 0) { lval->tv_sec+= lval->tv_usec / (1000L * 1000L) -1; lval->tv_usec-= (lval->tv_usec / (1000L * 1000L) -1) * (1000L * 1000L); } if (lval->tv_usec >= 1000L * 1000L) { lval->tv_sec+= lval->tv_usec / (1000L * 1000L); lval->tv_usec-= (lval->tv_usec / (1000L * 1000L)) * (1000L * 1000L); } } NetGame::NetGame(const string &server, const string &port, bool _blocking) { blocking = _blocking; recvbuf.len = recvbuf.pos = sendbuf.len = sendbuf.pos = 0; recvbuf.sz = sendbuf.sz = sizeof(struct netmessage); struct addrinfo criteria; memset(&criteria, 0, sizeof(criteria)); criteria.ai_family= AF_INET; criteria.ai_socktype= SOCK_STREAM; criteria.ai_protocol= IPPROTO_TCP; criteria.ai_flags= # ifndef WIN32 // only in vista, so we are disabling it for win32 completely AI_ADDRCONFIG | # endif AI_CANONNAME; // | AI_IDN | AI_CANONIDN struct addrinfo *peers; int stat; if ((stat= getaddrinfo(server.c_str(), port.c_str(), &criteria, &peers))) throw runtime_error(string("NetGame: could not get host address (") + gai_strerror(stat) + ")"); sock=-1; for (struct addrinfo *peer=peers; peer!=NULL; peer=peer->ai_next) { cout << "NetGame: Connecting to " << peer->ai_canonname; #ifdef WIN32 // what the hell did these guys smoke?? they introduced inet_ntop in vista, // BUT it's name is InetNtop. so we are using good old inet_ntoa const char *addrstr = inet_ntoa(((struct sockaddr_in *)(peer->ai_addr))->sin_addr); if (addrstr) cout << " (" << addrstr << ":" << ntohs(((struct sockaddr_in *)(peer->ai_addr))->sin_port) << ")" << endl; #else char addrstr[INET_ADDRSTRLEN]; if (inet_ntop(peer->ai_addr->sa_family, &((struct sockaddr_in *)(peer->ai_addr))->sin_addr, addrstr, INET_ADDRSTRLEN)) cout << " (" << addrstr << ":" << ntohs(((struct sockaddr_in *)(peer->ai_addr))->sin_port) << ")" << endl; #endif sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock <0 ) throw runtime_error(string("NetGame: could not create socket (") + strerror(errno) + ")"); if (connect(sock, peer->ai_addr, peer->ai_addrlen) == -1) { cerr << "NetGame: Connect failed (" << strerror(errno) << ")" << endl; CLOSE(sock); errno=0; sock=-1; continue; } break; } if (sock < 0) throw runtime_error("NetGame: Could not connect to server"); freeaddrinfo(peers); # ifdef WIN32 BOOL yes=TRUE; if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char*)&yes, sizeof(yes)) == (-1) || setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(yes)) == (-1) ) cerr << "NetGame: Socket options have not been set properly" << endl; # else int yes= 1, rcvlow= 2; int maxseg= sizeof(struct netmessage); uint8_t tos= IPTOS_LOWDELAY; if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == (-1) || // we expect to receive short integers setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, &rcvlow, sizeof(rcvlow)) == (-1) || setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == (-1) || // ideally, we don't send or receive more data than a netmessage contains setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &maxseg, sizeof(maxseg)) == (-1) || setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == (-1) ) cerr << "NetGame: Socket options have not been set properly" << endl; # endif cout << "NetGame: Connected." << endl; struct netmessage msg; int netcode = 0; // make sure to retrieve a full game state from the server when joining the game if ( (netcode= netmessage_recv(sock, &msg, sizeof(msg), &recvbuf)) == NETMSG_SUCCESS ) { switch (msg.hdr.id) { case NETMSG_STAT: gameplay_apply_state(&msg, &game, &team); break; case NETMSG_KICK: cout << "NetGame: Got kicked! Do you see what happens, Larry: " << msg.payload.data << endl; throw runtime_error(string("NetGame: Got kicked! Do you see what happens, Larry: ") + msg.payload.data); break; default: cerr << "NetGame: Received unknown message, id == " << msg.hdr.id << " ('" << (char)msg.hdr.id << "')" << endl; break; } } else throw runtime_error("NetGame: Cannot determine initial game state"); # ifdef DEBUG cout << "[Packet | Initial] Ball pos == ( " << game.ball.pos[0] << ", " << game.ball.pos[1] << " ), dir == ( " << game.ball.dir[0] << ", " << game.ball.dir[1] << " ), paddles == ( " << game.pad[0].mean << ", " << game.pad[1].mean << ", team == " << team << endl; # endif if (!blocking) { # ifdef WIN32 unsigned long nonblk=1; if (ioctlsocket(sock, FIONBIO, &nonblk)) cerr << "NetGame: Socket I/O is blocking" << endl; # else if (fcntl(sock, F_SETFL, O_NONBLOCK) == (-1)) cerr << "NetGame: Socket I/O is blocking" << endl; # endif } } NetGame::~NetGame() { CLOSE(sock); } void NetGame::update(const struct timeval *time_wait) { struct netmessage msg; int netcode = NETMSG_SUCCESS; bool recv=true; fd_set listen; FD_ZERO(&listen); #ifdef WIN32 FD_SET((SOCKET)sock, &listen); #else FD_SET(sock, &listen); #endif struct timeval time, time_start, time_diff; gettimeofday(&time_start, NULL); memcpy(&time, time_wait, sizeof(struct timeval)); //handle incoming messages while ( recv && (blocking || (select(sock +1, &listen, NULL, NULL, &time) > 0) ) ) { gettimeofday(&time_diff, NULL); timeval_combine(1, &time_diff, -1, &time_start); gettimeofday(&time_start, NULL); // check for time 'overflow' if (time.tv_sec < time_diff.tv_sec || (time.tv_sec == time_diff.tv_sec && time.tv_usec < time_diff.tv_usec) ) // no more time left :( { time.tv_sec = 0; time.tv_usec = 0; } else timeval_combine(1, &time, -1, &time_diff); netcode = netmessage_recv(sock, &msg, sizeof(msg), &recvbuf); if (netcode != NETMSG_SUCCESS) continue; switch (msg.hdr.id) { case NETMSG_STAT: case NETMSG_UPDT: gameplay_apply_state(&msg, &game, &team); break; case NETMSG_KICK: cout << "NetGame: Got kicked! Do you see what happens, Larry: " << msg.payload.data << endl; throw runtime_error(string("NetGame: Got kicked! Do you see what happens, Larry: ") + msg.payload.data); break; default: cerr << "NetGame: Received unknown message, id == " << msg.hdr.id << "('" << (char)msg.hdr.id << "')" << endl; break; } if (blocking) recv=false; } if ((netcode != NETMSG_SUCCESS) && (netcode != NETMSG_FAIL_DELIVER) && (netcode != NETMSG_PARTIAL)) { cerr << "NetGame: " << "Socket failure while reading (" << netcode << ")" << endl; throw runtime_error("NetGame: Socket failure while reading"); } } void NetGame::sendpos(float pos) { uint16_t sendpos = (uint16_t)(pos * PONG_RANGE_SPREAD); int netcode = netmessage_send(sock, NETMSG_POS, &sendpos, sizeof(sendpos), &sendbuf); if ( (netcode!= NETMSG_SUCCESS) && (netcode != NETMSG_FAIL_DELIVER) && (netcode != NETMSG_PARTIAL)) { cerr << "NetGame: " << "Socket failure while sending (" << netcode << ")" << endl; throw runtime_error("NetGame: Socket failure while sending"); } } void NetGame::sendpos_flush() { int netcode = netmessage_buffer_flush(sock, &sendbuf); if ( (netcode!= NETMSG_SUCCESS) && (netcode != NETMSG_FAIL_DELIVER) && (netcode != NETMSG_PARTIAL)) { cerr << "NetGame: " << "Socket failure while sending (" << netcode << ")" << endl; throw runtime_error("NetGame: Socket failure while sending"); } } mmpong-0.9.1/client_gl/renderer.h0000644000175000017500000000402511132501255015675 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __RENDERER_H__ #define __RENDERER_H__ #include #ifndef GL_GLEXT_VERSION # include #endif #include "game.h" class Renderer { private: SDL_Surface *surface; int win_w, win_h, full_w, full_h; bool full; bool sexy; int video_flags; GLfloat field_width; GLuint box_list, cylinder_list, ball_list; GLuint score_texture, field_texture; void init_graphics(); void draw_digit(uint16_t digit, float size); void draw_field(); void draw_paddle(float mean, float size, uint16_t team, uint16_t mode, uint16_t profile); void draw_players(uint16_t team, uint16_t number); void draw_score(uint16_t team, uint16_t score); void draw_variance(float mean, float var, uint16_t team); void resize(); public: Renderer(int _win_w, int _win_h, bool _full, bool _sexy, const std::string &respath, const std::string &title="Title"); ~Renderer(); void set_winsize(int _win_w, int _win_h) { win_w = _win_w; win_h = _win_h; resize(); }; void set_full(bool _full) { full = _full; resize(); }; bool get_full() { return full; }; void render(); void render(Game &game, float pos); int get_width() { return full ? full_w : win_w; }; int get_height() { return full ? full_h : win_h; }; }; #endif mmpong-0.9.1/client_gl/glhelper.cpp0000644000175000017500000001765411130242601016233 0ustar andreandre/* Copyright (C) 2008 Jan Friederich This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include "glhelper.h" void create_box(GLuint *list) { *list = glGenLists(1); glNewList(*list, GL_COMPILE); glBegin(GL_QUADS); // front face glNormal3f(0.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // back face glNormal3f(0.0f, 0.0f, -1.0f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); // right face glNormal3f(1.0f, 0.0f, 0.0f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, 0.5f); // left face glNormal3f(-1.0f, 0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // top face glNormal3f(0.0f, 1.0f, 0.0f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // bottom face glNormal3f(0.0f, -1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); glEndList(); } void create_cylinder(GLuint *list, const GLuint n) { const GLfloat step = 2.0f * M_PI / n; *list = glGenLists(1); glNewList(*list, GL_COMPILE); // top glBegin(GL_TRIANGLE_FAN); glNormal3f(0.0f, 0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 0.5f); for (GLuint k = 0; k < n+1; ++k) { glVertex3f(0.5f*cosf(step*k), 0.5f*sinf(step*k), 0.5f); } glEnd(); // bottom glBegin(GL_TRIANGLE_FAN); glNormal3f(0.0f, 0.0f, -1.0f); glVertex3f(0.0f, 0.0f, -0.5f); for (GLuint k = 0; k < n+1; ++k) { glVertex3f(0.5f*cosf(step*k), -0.5f*sinf(step*k), -0.5f); } glEnd(); // side glBegin(GL_QUAD_STRIP); for (GLuint k = 0; k < n+1; ++k) { glNormal3f(cosf(step*k), sinf(step*k), 0.0f); glVertex3f(0.5f*cosf(step*k), 0.5f*sinf(step*k), 0.5f); glVertex3f(0.5f*cosf(step*k), 0.5f*sinf(step*k), -0.5f); } glEnd(); glEndList(); } void create_ball(GLuint *list, const GLuint m, const GLuint n) { const GLfloat slice = 0.5f * M_PI / m; const GLfloat step = 2.0f * M_PI / n; const GLfloat r = 0.5f; GLfloat h1 = cosf(slice*(m-1)), h2; *list = glGenLists(1); glNewList(*list, GL_COMPILE); // top glBegin(GL_TRIANGLE_FAN); glNormal3f(0.0f, 0.0f, 1.0f); glVertex3f(0.0f, 0.0f, r); for (GLuint k = 0; k < n+1; ++k) { glNormal3f(h1*cosf(step*k), h1*sinf(step*k), sinf(slice*(m-1))); glVertex3f(r*h1*cosf(step*k), r*h1*sinf(step*k), r*sinf(slice*(m-1))); } glEnd(); // bottom glBegin(GL_TRIANGLE_FAN); glNormal3f(0.0f, 0.0f, -1.0f); glVertex3f(0.0f, 0.0f, -r); for (GLuint k = 0; k < n+1; ++k) { glNormal3f(h1*cosf(step*k), -h1*sinf(step*k), -sinf(slice*(m-1))); glVertex3f(r*h1*cosf(step*k), -r*h1*sinf(step*k), -r*sinf(slice*(m-1))); } glEnd(); // side glBegin(GL_QUAD_STRIP); for (GLint j = -m+1; j < (GLint)m-1; ++j) { h1 = cosf(slice*(j+1)); h2 = cosf(slice*j); for (GLuint k = 0; k < n+1; ++k) { glNormal3f(h1*cosf(step*k), h1*sinf(step*k), sinf(slice*(j+1))); glVertex3f(r*h1*cosf(step*k), r*h1*sinf(step*k), r*sinf(slice*(j+1))); glNormal3f(h2*cosf(step*k), h2*sinf(step*k), sinf(slice*j)); glVertex3f(r*h2*cosf(step*k), r*h2*sinf(step*k), r*sinf(slice*j)); } } glEnd(); glEndList(); } int load_png(GLuint *texture, const char *filename) { SDL_Surface *surface = NULL; GLuint channels = 0, format = 0; GLubyte *data = NULL; surface = IMG_Load(filename); if (surface == NULL) { fprintf(stderr, "load_png: Unable to load bitmap: %s\n", filename); return -1; } if ((surface = SDL_DisplayFormatAlpha(surface)) == NULL) { fprintf(stderr, "load_png: Unable to format bitmap: %s\n", filename); return -1; } channels = surface->format->BytesPerPixel; if (channels == 4) { //format = (surface->format->Rmask == 0xff) ? GL_RGBA : GL_BGRA; format = GL_RGBA; } else if (channels == 3) { //format = (surface->format->Rmask == 0xff) ? GL_RGB : GL_BGR; format = GL_RGB; } data = (GLubyte *)malloc(surface->w * surface->h * channels); if (data == NULL) { fprintf(stderr, "load_png: Unable to allocate memory: %s\n", filename); return -1; } for(int row = 0; row < surface->h; ++row) { memcpy(data + channels * surface->w * (surface->h - 1 - row), (char *)surface->pixels + row * surface->w * channels, surface->w * channels); } glGenTextures(1, texture); glBindTexture(GL_TEXTURE_2D, *texture); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, channels, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, data); free(data); SDL_FreeSurface(surface); return 0; } mmpong-0.9.1/client_gl/netgame.h0000644000175000017500000000247411132651464015525 0ustar andreandre/* Copyright (C) 2008 André Gaul, Jan Friederich, Steffen Basting, Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __NETGAME_H__ #define __NETGAME_H__ #include #include "lib/message.h" #include "game.h" void timeval_combine(const int lfact, struct timeval *lval, const int rfact, const struct timeval *rval); class NetGame : public Game { private: int sock; bool blocking; struct netmessage_buffer recvbuf, sendbuf; public: NetGame(const std::string &server, const std::string &port, bool _blocking); ~NetGame(); void update(const struct timeval *time_wait); void sendpos(float pos); void sendpos_flush(); }; #endif mmpong-0.9.1/client_gl/gui.cpp0000644000175000017500000003143011132741000015200 0ustar andreandre/* Copyright (C) 2008 Jan Friederich, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include "client.h" #include "gui.h" using namespace CEGUI; using namespace std; #define GUI_ALPHA 0.7 #define GUI_ALPHA_BG 0.3 // Translate a SDLKey to the proper CEGUI::Key static CEGUI::uint SDLKeyToCEGUIKey(SDLKey key) { switch (key) { case SDLK_BACKSPACE: return Key::Backspace; case SDLK_TAB: return Key::Tab; case SDLK_RETURN: return Key::Return; case SDLK_PAUSE: return Key::Pause; case SDLK_ESCAPE: return Key::Escape; case SDLK_SPACE: return Key::Space; case SDLK_COMMA: return Key::Comma; case SDLK_MINUS: return Key::Minus; case SDLK_PERIOD: return Key::Period; case SDLK_SLASH: return Key::Slash; case SDLK_0: return Key::Zero; case SDLK_1: return Key::One; case SDLK_2: return Key::Two; case SDLK_3: return Key::Three; case SDLK_4: return Key::Four; case SDLK_5: return Key::Five; case SDLK_6: return Key::Six; case SDLK_7: return Key::Seven; case SDLK_8: return Key::Eight; case SDLK_9: return Key::Nine; case SDLK_COLON: return Key::Colon; case SDLK_SEMICOLON: return Key::Semicolon; case SDLK_EQUALS: return Key::Equals; case SDLK_LEFTBRACKET: return Key::LeftBracket; case SDLK_BACKSLASH: return Key::Backslash; case SDLK_RIGHTBRACKET: return Key::RightBracket; case SDLK_a: return Key::A; case SDLK_b: return Key::B; case SDLK_c: return Key::C; case SDLK_d: return Key::D; case SDLK_e: return Key::E; case SDLK_f: return Key::F; case SDLK_g: return Key::G; case SDLK_h: return Key::H; case SDLK_i: return Key::I; case SDLK_j: return Key::J; case SDLK_k: return Key::K; case SDLK_l: return Key::L; case SDLK_m: return Key::M; case SDLK_n: return Key::N; case SDLK_o: return Key::O; case SDLK_p: return Key::P; case SDLK_q: return Key::Q; case SDLK_r: return Key::R; case SDLK_s: return Key::S; case SDLK_t: return Key::T; case SDLK_u: return Key::U; case SDLK_v: return Key::V; case SDLK_w: return Key::W; case SDLK_x: return Key::X; case SDLK_y: return Key::Y; case SDLK_z: return Key::Z; case SDLK_DELETE: return Key::Delete; case SDLK_KP0: return Key::Numpad0; case SDLK_KP1: return Key::Numpad1; case SDLK_KP2: return Key::Numpad2; case SDLK_KP3: return Key::Numpad3; case SDLK_KP4: return Key::Numpad4; case SDLK_KP5: return Key::Numpad5; case SDLK_KP6: return Key::Numpad6; case SDLK_KP7: return Key::Numpad7; case SDLK_KP8: return Key::Numpad8; case SDLK_KP9: return Key::Numpad9; case SDLK_KP_PERIOD: return Key::Decimal; case SDLK_KP_DIVIDE: return Key::Divide; case SDLK_KP_MULTIPLY: return Key::Multiply; case SDLK_KP_MINUS: return Key::Subtract; case SDLK_KP_PLUS: return Key::Add; case SDLK_KP_ENTER: return Key::NumpadEnter; case SDLK_KP_EQUALS: return Key::NumpadEquals; case SDLK_UP: return Key::ArrowUp; case SDLK_DOWN: return Key::ArrowDown; case SDLK_RIGHT: return Key::ArrowRight; case SDLK_LEFT: return Key::ArrowLeft; case SDLK_INSERT: return Key::Insert; case SDLK_HOME: return Key::Home; case SDLK_END: return Key::End; case SDLK_PAGEUP: return Key::PageUp; case SDLK_PAGEDOWN: return Key::PageDown; case SDLK_F1: return Key::F1; case SDLK_F2: return Key::F2; case SDLK_F3: return Key::F3; case SDLK_F4: return Key::F4; case SDLK_F5: return Key::F5; case SDLK_F6: return Key::F6; case SDLK_F7: return Key::F7; case SDLK_F8: return Key::F8; case SDLK_F9: return Key::F9; case SDLK_F10: return Key::F10; case SDLK_F11: return Key::F11; case SDLK_F12: return Key::F12; case SDLK_F13: return Key::F13; case SDLK_F14: return Key::F14; case SDLK_F15: return Key::F15; case SDLK_NUMLOCK: return Key::NumLock; case SDLK_SCROLLOCK: return Key::ScrollLock; case SDLK_RSHIFT: return Key::RightShift; case SDLK_LSHIFT: return Key::LeftShift; case SDLK_RCTRL: return Key::RightControl; case SDLK_LCTRL: return Key::LeftControl; case SDLK_RALT: return Key::RightAlt; case SDLK_LALT: return Key::LeftAlt; case SDLK_LSUPER: return Key::LeftWindows; case SDLK_RSUPER: return Key::RightWindows; case SDLK_SYSREQ: return Key::SysRq; case SDLK_MENU: return Key::AppMenu; case SDLK_POWER: return Key::Power; default: return 0; } return 0; } GUI::GUI(std::string resbase, int w, int h, bool _visible, ClientState *_state) { renderer = new OpenGLRenderer(0, w, h); log = new dummyLogger(); sys = new System(renderer); visible = _visible; state = _state; try { DefaultResourceProvider *rp = static_cast(sys->getResourceProvider()); rp->setResourceGroupDirectory("schemes", resbase + "CEGUI/schemes/"); rp->setResourceGroupDirectory("imagesets", resbase + "CEGUI/imagesets/"); rp->setResourceGroupDirectory("fonts", resbase + "CEGUI/fonts/"); rp->setResourceGroupDirectory("layouts", resbase + "CEGUI/layouts/"); rp->setResourceGroupDirectory("looknfeels", resbase + "CEGUI/looknfeels/"); if( sys->getDefaultXMLParserName().compare( "XercesParser" ) == 0 ) rp->setResourceGroupDirectory( "schemas", "CEGUI/gameschemes/"); Scheme::setDefaultResourceGroup("schemes"); Imageset::setDefaultResourceGroup("imagesets"); Font::setDefaultResourceGroup("fonts"); WindowManager::setDefaultResourceGroup("layouts"); WidgetLookManager::setDefaultResourceGroup("looknfeels"); // now get a dummy window // ATTENTION (by andré): // the next line segfaults with CEGUI 0.5.0-4.1 in Debian/Ubuntu // this is fixed in 0.6 (sorry, found no workaround atm) SchemeManager::getSingleton().loadScheme( "TaharezLook.scheme" ); sys->setDefaultMouseCursor( "TaharezLook", "MouseArrow" ); sys->setDefaultTooltip("TaharezLook/Tooltip"); WindowManager *wmgr = WindowManager::getSingletonPtr(); Window *root = wmgr->loadWindowLayout("mmpong-gl.layout"); root->setAlpha(GUI_ALPHA ); sys->setGUISheet( root ); win_config = wmgr->getWindow("WindowConfig"); win_config->subscribeEvent(FrameWindow::EventCloseClicked, CEGUI::Event::Subscriber(&GUI::on_cancel, this)); edit_server = static_cast (wmgr->getWindow("EditboxServer")); edit_server->setText(state->server); edit_port = static_cast (wmgr->getWindow("EditboxPort")); edit_port->setText(state->port); check_sexy = static_cast (wmgr->getWindow("CheckboxSexy")); check_sexy->setSelected(state->sexy); check_sexy->subscribeEvent(Checkbox::EventCheckStateChanged, CEGUI::Event::Subscriber(&GUI::on_sexy, this)); check_sound = static_cast (wmgr->getWindow("CheckboxSound")); check_sound->setSelected(state->sound); check_sound->subscribeEvent(Checkbox::EventCheckStateChanged, CEGUI::Event::Subscriber(&GUI::on_sound, this)); check_full = static_cast (wmgr->getWindow("CheckboxFull")); check_full->setSelected(state->renderer->get_full()); check_full->subscribeEvent(Checkbox::EventCheckStateChanged, CEGUI::Event::Subscriber(&GUI::on_full, this)); CEGUI::PushButton *btn; btn = static_cast (wmgr->getWindow("ButtonQuit")); btn->subscribeEvent(PushButton::EventClicked, CEGUI::Event::Subscriber(&GUI::on_quit, this)); btn = static_cast (wmgr->getWindow("ButtonCancel")); btn->subscribeEvent(PushButton::EventClicked, CEGUI::Event::Subscriber(&GUI::on_cancel, this)); btn = static_cast (wmgr->getWindow("ButtonConnect")); btn->subscribeEvent(PushButton::EventClicked, CEGUI::Event::Subscriber(&GUI::on_connect, this)); // dialog win_dialog = wmgr->getWindow("WindowDialog"); stext_dialog = wmgr->getWindow("StaticTextDialog"); btn = static_cast (wmgr->getWindow("ButtonDialogOk")); btn->subscribeEvent(PushButton::EventClicked, CEGUI::Event::Subscriber(&GUI::on_dialog_ok, this)); } catch(CEGUI::Exception &ex) { printf("Exception: %s\n", ex.getMessage().c_str()); } SDL_ShowCursor(SDL_DISABLE); SDL_EnableUNICODE(1); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); } void GUI::resize_init() { renderer->grabTextures(); } void GUI::resize_finish(int w, int h) { renderer->restoreTextures(); renderer->setDisplaySize(Size(w, h)); } void GUI::handle_mouse_down(Uint8 button) { switch (button) { case SDL_BUTTON_LEFT: sys->injectMouseButtonDown(CEGUI::LeftButton); break; case SDL_BUTTON_MIDDLE: sys->injectMouseButtonDown(CEGUI::MiddleButton); break; case SDL_BUTTON_RIGHT: sys->injectMouseButtonDown(CEGUI::RightButton); break; case SDL_BUTTON_WHEELDOWN: sys->injectMouseWheelChange(-1); break; case SDL_BUTTON_WHEELUP: sys->injectMouseWheelChange(1); break; } } void GUI::handle_mouse_up(Uint8 button) { switch (button) { case SDL_BUTTON_LEFT: sys->injectMouseButtonUp(CEGUI::LeftButton); break; case SDL_BUTTON_MIDDLE: sys->injectMouseButtonUp(CEGUI::MiddleButton); break; case SDL_BUTTON_RIGHT: sys->injectMouseButtonUp(CEGUI::RightButton); break; } } int GUI::handle_event(SDL_Event &e) { CEGUI::uint kc; switch(e.type) { // mouse case SDL_MOUSEMOTION: sys->injectMousePosition(e.motion.x, e.motion.y); break; case SDL_MOUSEBUTTONDOWN: handle_mouse_down(e.button.button); break; case SDL_MOUSEBUTTONUP: handle_mouse_up(e.button.button); break; //keyboard case SDL_KEYDOWN: kc = SDLKeyToCEGUIKey(e.key.keysym.sym); sys->injectKeyDown(kc); if (e.key.keysym.unicode) sys->injectChar(e.key.keysym.unicode); break; case SDL_KEYUP: sys->injectKeyUp(e.key.keysym.scancode); break; } return 0; } void GUI::render() { //inject time double t = 0.001 * SDL_GetTicks(); sys->injectTimePulse( t - last_time ); last_time = t; if (visible) sys->renderGUI(); } bool GUI::on_quit(const CEGUI::EventArgs &e) { state->signal_exiting = 1; return true; } bool GUI::on_cancel(const CEGUI::EventArgs &e) { visible = false; return true; } bool GUI::on_connect(const CEGUI::EventArgs &e) { delete state->game; state->game = NULL; try { state->server = edit_server->getText().c_str(); state->port = edit_port->getText().c_str(); state->game = new NetGame(state->server, state->port, 0); set_visible(false); } catch (runtime_error &err) { cerr << err.what() << endl; show_dialog("Error", err.what()); delete state->game; state->game = NULL; } return true; } bool GUI::on_sexy(const CEGUI::EventArgs &e) { state->sexy = !state->sexy; return true; } bool GUI::on_sound(const CEGUI::EventArgs &e) { state->sound = !state->sound; return true; } bool GUI::on_full(const CEGUI::EventArgs &e) { state->full = !state->full; resize_init(); state->renderer->set_full(state->full); resize_finish(state->renderer->get_width(), state->renderer->get_height()); return true; } bool GUI::on_dialog_ok(const CEGUI::EventArgs &e) { win_dialog->setModalState(false); win_dialog->setVisible(false); win_config->setAlpha(GUI_ALPHA); return true; } void GUI::show_dialog(const std::string &title, const std::string &message) { win_config->setAlpha(GUI_ALPHA_BG); win_dialog->setText(title); stext_dialog->setText(message); win_dialog->setVisible(true); win_dialog->setModalState(true); } void GUI::set_full(bool _full) { check_full->setSelected(_full); /* if (check_full->isSelected() != state->renderer->get_full()) on_full(CEGUI::EventArgs()); */ } mmpong-0.9.1/COPYING0000644000175000017500000010451311124222165013015 0ustar andreandre GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . mmpong-0.9.1/client_caca/0000755000175000017500000000000011354734767014230 5ustar andreandremmpong-0.9.1/client_caca/config.h.in0000644000175000017500000000150611131661577016244 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __VERSION_H__ #define __VERSION_H__ #define VER_MAJ 0 #define VER_MIN 9 #define VER_SVN "${SVN_REV}" #define RESPATH "${CLIENT_CACA_RES_PATH}" #endif mmpong-0.9.1/client_caca/client.h0000644000175000017500000000131511121720206015626 0ustar andreandre/* Copyright (C) 2008 Steffen Basting This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #define BALL_STRING \ "**" \ "*." mmpong-0.9.1/client_caca/CMakeLists.txt0000644000175000017500000000130111132703607016742 0ustar andreandreSET( CLIENT_CACA_SOURCES client.c ) SET( SVN_REV "${MMPONG_WC_REVISION}" ) SET( CONFIGURE_DIR "${CMAKE_CURRENT_BINARY_DIR}") CONFIGURE_FILE ( config.h.in ${CONFIGURE_DIR}/config.h ) FIND_PACKAGE(PkgConfig REQUIRED) PKG_SEARCH_MODULE( CACA caca) IF(CACA_FOUND) MESSAGE(STATUS "found libcaca: enabling client-caca.") ADD_EXECUTABLE( mmpong-caca ${CLIENT_CACA_SOURCES}) INCLUDE_DIRECTORIES( mmpong-caca ${INC_DIR_LIB} ${CACA_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CONFIGURE_DIR}) TARGET_LINK_LIBRARIES( mmpong-caca libmmpong ${CACA_LIBRARIES}) INSTALL(TARGETS mmpong-caca RUNTIME DESTINATION games) ELSE(CACA_FOUND) MESSAGE(STATUS "could not find libcaca: disabling client-caca.") ENDIF(CACA_FOUND) mmpong-0.9.1/client_caca/client.c0000644000175000017500000003715211131740172015636 0ustar andreandre/* Copyright (C) 2008 Steffen Basting, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifdef WIN32 # include # include #else # include # include # include # include # include #endif #include #include #include #include #include #include #include #include #include "lib/message.h" #include "lib/game.h" #define PONG_KICKED (-1) #define PONG_FAILURE (-2) #define RTTM_QUERY_INTERVAL 12000 // every 12sec #define DRAW_REFRESH_INTERVAL 25 // every 25msec #define INFOBARCOLOR CACA_BLACK #define BACKCOLOR CACA_DARKGRAY #define BALL_COLOR CACA_YELLOW #define PADLEFT_COLOR CACA_BLUE #define PADRIGHT_COLOR CACA_RED #define PADHIGHLIGHT_COLOR CACA_WHITE #define SCORESTR "SCORE" short signal_exiting= 0; struct gameplay *game= NULL; #ifndef WIN32 void sigterm_handler(int signal) { if (signal == SIGPIPE) printf("Caught SIGPIPE.\n"); if (signal == SIGURG) printf("Caught SIGURG.\n"); if (signal == SIGTERM) signal_exiting= 1; } #endif static void free_game() { if (game) free(game); } void draw_paddle(caca_canvas_t *cv, const struct gamepaddle *pad, uint16_t team, uint8_t padcolor) { caca_set_color_ansi(cv, padcolor, BACKCOLOR); float mean = ( 1 - pad->mean ) * (caca_get_canvas_height(cv) -1), size = pad->size * (caca_get_canvas_height(cv) -1); if (mean + size/2 > (caca_get_canvas_height(cv) -1)) { #ifdef DEBUG fprintf(stderr, "Paddle[%d] out of bounds (%f).\n", team, pad->mean); #endif mean = caca_get_canvas_height(cv)-size/2; } if (mean < size/2) { #ifdef DEBUG fprintf(stderr, "Paddle[%d] out of bounds (%f).\n", team, pad->mean); #endif mean = size/2; } if ( !(size/2)) { #ifdef DEBUG fprintf(stderr, "Paddle[%d] size == 0.\n", team); #endif size=2; } for (uint16_t ypos = mean - size/2; ypos < mean + size/2; ypos++) caca_put_str(cv, (team==0 ? 0 : caca_get_canvas_width(cv)-1), ypos, "#"); } void draw_everything(caca_canvas_t *cv, const struct gameplay *game, float pos, uint16_t team) { caca_set_color_ansi(cv, BACKCOLOR, BACKCOLOR); caca_clear_canvas(cv); // ball caca_set_color_ansi(cv, BALL_COLOR, BACKCOLOR); uint16_t x = game->ball.pos[0] * caca_get_canvas_width(cv), y = (1 - game->ball.pos[1]) * (caca_get_canvas_height(cv) - 1) +1; caca_put_str(cv, x , y, "o"); // paddles uint8_t colors[2]= { PADLEFT_COLOR, PADRIGHT_COLOR }; for (unsigned side= 0; side< 2; side++) draw_paddle(cv, game->pad+side, side, colors[side]); struct gamepaddle mypad= { .size= game->pad[team].size, .mean= pos, .var= 0 }; draw_paddle(cv, &mypad, team, PADHIGHLIGHT_COLOR); // score caca_set_color_ansi(cv, PADHIGHLIGHT_COLOR, INFOBARCOLOR); caca_fill_box(cv, 0, 0, caca_get_canvas_width(cv), 1, ' '); caca_put_str(cv, (caca_get_canvas_width(cv) - strlen(SCORESTR)) /2, 0, SCORESTR); for (unsigned side= 0; side< 2; side++) { caca_set_color_ansi(cv, colors[side], INFOBARCOLOR); int putlen= snprintf(NULL, 0, "%u", game->pad_attr[side].score) +1; char *putscore= malloc(putlen); if (!putscore) continue; snprintf(putscore, putlen, "%u", game->pad_attr[side].score); caca_put_str(cv, (side)? (caca_get_canvas_width(cv) -putlen +1) : 0, 0, putscore); free(putscore); } } static void timeval_combine(const int lfact, struct timeval *lval, const int rfact, const struct timeval *rval) { // combine linearly lval->tv_sec= lval->tv_sec * lfact + rval->tv_sec * rfact; lval->tv_usec= lval->tv_usec * lfact + rval->tv_usec * rfact; // normalize if (lval->tv_usec < 0) { lval->tv_sec+= lval->tv_usec / (1000L * 1000L) -1; lval->tv_usec-= (lval->tv_usec / (1000L * 1000L) -1) * (1000L * 1000L); } if (lval->tv_usec >= 1000L * 1000L) { lval->tv_sec+= lval->tv_usec / (1000L * 1000L); lval->tv_usec-= (lval->tv_usec / (1000L * 1000L)) * (1000L * 1000L); } } int main(int argc, char *argv[]) { caca_canvas_t *cv; caca_display_t *dp; if (argc <= 1) { printf("Usage: %s server [port]\n", argv[0]); exit(1); } #ifndef WIN32 struct sigaction termact= { .sa_handler= sigterm_handler, .sa_flags= SA_RESETHAND }; sigemptyset(&termact.sa_mask); if ( (sigaction(SIGTERM, &termact, NULL)) || (termact.sa_flags= SA_RESTART, sigaction(SIGPIPE, &termact, NULL)) ) { fprintf(stderr, "Error putting signal handlers in place.\n"); exit(-1); } #else // winsock init WSADATA wsaData; int wsaresult = WSAStartup(MAKEWORD(2,2), &wsaData); if (wsaresult != 0) { fprintf(stderr, "Error initializing Windows Sockets (code==%d)\n", wsaresult); exit(-1); } #endif struct netmessage_buffer *recvbuf= NULL, *sendbuf= NULL; if ( (netmessage_buffer_init(&recvbuf) != NETMSG_SUCCESS) || (netmessage_buffer_init(&sendbuf) != NETMSG_SUCCESS) ) { fprintf(stderr, "Error initializing message buffers.\n"); exit(-1); } netmessage_scrambler_init(); // conceal the real system clock char *portname= "1212"; if (argc > 2) portname= argv[2]; struct addrinfo criteria= { .ai_family= AF_INET, .ai_socktype= SOCK_STREAM, .ai_protocol= IPPROTO_TCP, .ai_flags= AI_ADDRCONFIG | AI_CANONNAME // | AI_IDN | AI_CANONIDN }; struct addrinfo *peers; int stat; if ((stat= getaddrinfo(argv[1], portname, &criteria, &peers))) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(stat)); exit(-1); } char addrstr[INET_ADDRSTRLEN]; printf("Connecting to %s", peers->ai_canonname); if (inet_ntop(peers->ai_addr->sa_family, &((struct sockaddr_in *)(peers->ai_addr))->sin_addr, addrstr, INET_ADDRSTRLEN)) printf(" (%s:%d)", addrstr, ntohs(((struct sockaddr_in *)(peers->ai_addr))->sin_port)); printf("\n"); int sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == (-1)) { perror("socket()"); exit(-1); } if (connect(sock, peers->ai_addr, sizeof(*peers->ai_addr)) == (-1)) { perror("socket()"); exit(-1); } int yes= 1, rcvlow= 2; int maxseg= sizeof(struct netmessage); uint8_t tos= IPTOS_LOWDELAY; if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == (-1) || // we expect to receive short integers setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, &rcvlow, sizeof(rcvlow)) == (-1) || setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == (-1) || // ideally, we don't send or receive more data than a netmessage contains setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &maxseg, sizeof(maxseg)) == (-1) || setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == (-1) ) fprintf(stderr, "Socket options have not been set properly.\n"); printf("Connected.\n"); cv = caca_create_canvas(0, 0); if(cv == NULL) { printf("Can't create canvas\n"); return -1; } dp = caca_create_display(cv); uint16_t team= 0; float pos= .5f; caca_set_display_time(dp, 800); caca_set_mouse(dp, 0); if (gameplay_getversion() < sizeof(struct gameplay)) { fprintf(stderr, "This client requires a newer version of the mmpong library (>=%u).\n", (unsigned)sizeof(struct gameplay)); exit(PONG_FAILURE); } if (atexit(free_game)) { fprintf(stderr, "Error adding an exit() handler.\n"); exit(PONG_FAILURE); } // The reason we do this is to have the game state managed exclusively by the lib if ((game= gameplay_create()) == NULL) { fprintf(stderr, "Cannot allocate game state.\n"); exit(PONG_FAILURE); } // Try setting sensible default values if (gameplay_init(game, game->version, gamemode_linear, gamepadprofile_flat) != PONG_SUCCESS) fprintf(stderr, "Warning: Init to default game state unsuccessful.\n"); struct netmessage msg; int netcode= 0; // make sure to retrieve a full game state from the server when joining the game if ( (netcode= netmessage_recv(sock, &msg, sizeof(msg), recvbuf)) == NETMSG_SUCCESS ) { switch (msg.hdr.id) { case NETMSG_STAT: gameplay_apply_state(&msg, game, &team); break; case NETMSG_KICK: printf("Got kicked! Do you see what happens, Larry: %s\n", msg.payload.data); exit(PONG_KICKED); break; default: fprintf(stderr, "Received unknown message, id == %d ('%c')\n", msg.hdr.id, (msg.hdr.id > ' ')? msg.hdr.id : ' '); exit(PONG_FAILURE); break; } } else { fprintf(stderr, "Cannot determine initial game state (code == %d)\n", netcode); exit(PONG_FAILURE); } // determine offset struct timeval peer_offset, relative; gettimeofday(&peer_offset, NULL); memcpy(&game->stamp, &peer_offset, sizeof(game->stamp)); memcpy(&game->lasthit, &game->stamp, sizeof(game->lasthit)); netmessage_get_hdr_stamp(&msg, &relative); timeval_combine(1, &peer_offset, (-1), &relative); printf("Client to server time offset: "); if (peer_offset.tv_sec > 60*60*24) printf("%ldd ", (long)peer_offset.tv_sec / (60*60*24)); if (peer_offset.tv_sec % (60*60*24) > 60*60) printf("%ldh ", (long)(peer_offset.tv_sec % (60*60*24)) / (60*60)); if (peer_offset.tv_sec % (60*60) > 60) printf("%ldm ", (long)(peer_offset.tv_sec % (60*60)) / 60); printf("%ld.%06lds%s\n", (peer_offset.tv_sec <0)? (long)peer_offset.tv_sec : (long)(peer_offset.tv_sec % 60), peer_offset.tv_usec, (peer_offset.tv_sec > 60*60*24)? " (Scrambler Active)" : ""); int adjusted_delay= 0, send_ping= 1; #ifdef DEBUG printf("[Packet | Initial] Ball pos == ( %1.2f, %1.2f ), dir == ( %1.2f, %1.2f ), paddles == { %1.2f, %1.2f }, team == %1d\n", game->ball.pos[0], game->ball.pos[1], game->ball.dir[0], game->ball.dir[1], game->pad[0].mean, game->pad[1].mean, team); #endif if (fcntl(sock, F_SETFL, O_NONBLOCK) == (-1)) fprintf(stderr, "Socket I/O is blocking.\n"); // network delay struct timeval reference, peer_delay; memset(&peer_delay, 0, sizeof(peer_delay)); uint16_t rttmpos= UINT16_MAX; // gettimeofday(&reference, NULL); // indisputable state unsigned server_score[2]= { game->pad_attr[0].score, game->pad_attr[1].score }; struct timeval caca_timer; memset(&caca_timer, 0, sizeof(caca_timer)); caca_event_t ev; while(!signal_exiting) { // measure network lag periodically if (send_ping == 0) { gettimeofday(&relative, NULL); timeval_combine(1, &relative, (-1), &reference); if (relative.tv_sec> RTTM_QUERY_INTERVAL / 1000L) send_ping= 1; if ((relative.tv_sec == RTTM_QUERY_INTERVAL / 1000L) && (relative.tv_usec / 1000L >= RTTM_QUERY_INTERVAL % 1000L)) send_ping= 1; } if (send_ping == 1) { if (netmessage_send(sock, NETMSG_POS, &rttmpos, sizeof(rttmpos), sendbuf) != NETMSG_SUCCESS) send_ping= (-1); gettimeofday(&reference, NULL); send_ping= 2; } // handle incoming messages while ( (netcode = netmessage_recv(sock, &msg, sizeof(msg), recvbuf)) == NETMSG_SUCCESS) { #ifdef DEBUG printf("Receiving.\n"); #endif switch (msg.hdr.id) { case NETMSG_STAT: case NETMSG_UPDT: gameplay_apply_state(&msg, game, &team); // system clock offset (game state update ---> send delay is implicit) timeval_combine(1, &game->stamp, 1, &peer_offset); if (adjusted_delay) // network lag timeval_combine(1, &game->stamp, -1, &peer_delay); memcpy(&game->lasthit, &game->stamp, sizeof(game->lasthit)); // save score for (int idx= 0; idx< 2; idx++) server_score[idx]= game->pad_attr[idx].score; break; case NETMSG_POS: // near-synchronous ping reply if ((send_ping != 2) || (msg.payload.position != rttmpos)) { fprintf(stderr, "Received irregular message (position %d).\n", msg.payload.position); break; } gettimeofday(&relative, NULL); timeval_combine(-1, &reference, 1, &relative); reference.tv_sec>>= 1; // two ways ---> one way reference.tv_usec>>= 1; memcpy(&peer_delay, &reference, sizeof(peer_delay)); memcpy(&reference, &relative, sizeof(reference)); printf("Measured network delay: %ld.%06lds\n", (long)peer_delay.tv_sec, (long)peer_delay.tv_usec); send_ping= 0; if (!adjusted_delay) { // determine the net clock offset peer_offset.tv_sec-= peer_delay.tv_sec; peer_offset.tv_usec-= peer_delay.tv_usec; if (peer_offset.tv_usec <0) { peer_offset.tv_usec+= 1000L * 1000L; peer_offset.tv_sec--; } adjusted_delay= 1; } break; case NETMSG_KICK: printf("Got kicked! Do you see what happens, Larry: %s\n", msg.payload.data); exit(PONG_KICKED); signal_exiting= 1; break; default: fprintf(stderr, "Received unknown message, id == %d ('%c').\n", msg.hdr.id, (msg.hdr.id > ' ')? msg.hdr.id : ' '); break; } } if ((netcode != NETMSG_SUCCESS) && (netcode != NETMSG_FAIL_DELIVER) && (netcode != NETMSG_PARTIAL)) { fprintf(stderr, "Socket failure while reading (%d).\n", netcode); exit(PONG_FAILURE); } if (game->status == gamestatus_running) { // adjust game gettimeofday(&relative, NULL); gameplay_update(game, &relative); // restore score for (int idx= 0; idx< 2; idx++) game->pad_attr[idx].score= server_score[idx]; } if ((caca_timer.tv_sec == 0) && (caca_timer.tv_usec < 1000)) { // render game #ifdef DEBUG printf("Drawing.\n"); #endif draw_everything(cv, game, pos, team); caca_refresh_display(dp); caca_timer.tv_usec= 1000L * DRAW_REFRESH_INTERVAL; } // try flushing send buffers if (sendbuf->len) netmessage_buffer_flush(sock, sendbuf); // wait for either a signal on the line or the refresh timer to expire fd_set listen; FD_ZERO(&listen); FD_SET(sock, &listen); int ret= select(sock +1, &listen, NULL, NULL, &caca_timer); if ((ret == (-1)) && (errno != EINTR)) { perror("select()"); if (caca_timer.tv_usec > 1000) continue; } if ((ret == 1) && (send_ping == 2)) continue; // hot phase of delay measurement // handle user events int newpos=0; while (caca_get_event(dp, CACA_EVENT_ANY, &ev, 0)) { switch (caca_get_event_type(&ev)) { case CACA_EVENT_RESIZE: draw_everything(cv, game, pos, team); break; case CACA_EVENT_KEY_PRESS: if (caca_get_event_key_ch(&ev) == 'q') signal_exiting=1; if (caca_get_event_key_ch(&ev) == CACA_KEY_UP) { pos += (game->pad[team].size / 2)? game->pad[team].size / 2 : .1f; newpos = 1; } if (caca_get_event_key_ch(&ev) == CACA_KEY_DOWN) { pos -= (game->pad[team].size / 2)? game->pad[team].size / 2 : .1f; newpos = 1; } break; case CACA_EVENT_MOUSE_MOTION: pos = 1 - ((float)caca_get_mouse_y(dp)) / caca_get_canvas_height(cv); newpos = 1; break; default: break; } } // notify the server if (newpos) { #ifdef DEBUG printf("Sending.\n"); #endif if (pos + game->pad[team].size / 2 > 1.f) pos = 1.f - game->pad[team].size / 2; if (pos - game->pad[team].size / 2 < 0.f) pos = game->pad[team].size / 2; uint16_t sendpos = pos * PONG_RANGE_SPREAD; int sendcode = netmessage_send(sock, NETMSG_POS, &sendpos, sizeof(sendpos), sendbuf); if ( (sendcode!= NETMSG_SUCCESS) && (sendcode != NETMSG_FAIL_DELIVER) && (sendcode != NETMSG_PARTIAL)) { printf("Socket failure while sending (%d).\n", sendcode); break; } } } caca_free_display(dp); caca_free_canvas(cv); netmessage_send(sock, NETMSG_EXIT, NULL, 0, sendbuf); freeaddrinfo(peers); close(sock); #ifdef WIN32 WSACLeanup(); #endif if (recvbuf) free(recvbuf); if (sendbuf) free(sendbuf); return 0; } mmpong-0.9.1/README.CMake0000644000175000017500000000257311132651464013633 0ustar andreandreContents -------- 1) General Notes 2) GNU/Linux 3) Mac OS X 4) Windows 1) General Notes ---------------- If you have Problems compiling mmpong with the hand-crafted Makefile (or you are compiling on Mac OS X or Windows), give cmake a try. In the source tree (that is where the folders 'lib', 'server', 'client_caca', 'client_gl' and friends reside) run: mkdir cmake_build cd cmake_build cmake .. This performs some checks like finding libraries and headers. If it fails you should check if all dependencies are installed and you may want to run ccmake .. to enter missing information for cmake. If everything is fine you can compile mmpong with make You perhaps want to install mmpong but sometimes you just want a quick game and run it directly from your cmake_build directory. 2) GNU/Linux No special remarks here. 3) Mac OS X ----------- To compile as a Universal Binary run: cmake ../mmpong -DCMAKE_OSX_ARCHITECTURES="ppc;i386" for CEGUI you have to set the LIBRARIES variable to something like this: -bind_at_load -framework CEGUI -framework CEGUIOpenGLRenderer -F/Library/Frameworks/CEGUI.framework/Resources/ -weak_framework freetype2 -weak_framework PCRE 4) Windows ---------- Note: Only Windows XP and above is supported! You propably want to setup a MinGW+MSYS environment to build mmpong for Windows. This results in headache. You have been warned! mmpong-0.9.1/test/0000755000175000017500000000000011354734767012762 5ustar andreandremmpong-0.9.1/test/sizeof.c0000644000175000017500000000046511127161437014414 0ustar andreandre#include #ifndef WIN32 # include #endif int main(void) { printf("\ #define __UNSIGNED_SIZE__ %d\n\ #define __VOID_PTR_SIZE__ %d\n\ #define __IN_ADDR_T_SIZE__ %d\n", sizeof(unsigned), sizeof(void *), #ifndef WIN32 sizeof(in_addr_t) #else sizeof(void *)+1 #endif ); return 0; } mmpong-0.9.1/test/msgclient.c0000644000175000017500000000370411124424132015070 0ustar andreandre#include #include #include #include #include #include #include #include #include #include "message.h" #define BUFSIZE 128 #define PORT 4711 int main(int argc, char **argv) { errno=0; if (argc < 2) { printf("Usage: %s address\n", argv[0]); exit(1); } int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0 || errno) { perror("socket()"); exit(2); } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); if (!inet_aton (argv[1], &addr.sin_addr)) { fprintf(stderr, "inet_aton(): address %s invalid\n", argv[1]); exit(3); } if ( connect(sock, (struct sockaddr *)&addr, sizeof(addr)) || errno) { perror("connect()"); exit(4); } if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { perror("fcntl(NONBLOCK)"); exit(5); } struct netmessage_buffer *recvbuf = NULL; if (netmessage_buffer_init(&recvbuf) != NETMSG_SUCCESS) { fprintf(stderr, "netmessage_buffer_init(): could not init buffer\n"); exit(6); } for (unsigned i=0; i<100000; i++) { // usleep(49000); struct netmessage msg; int ret = recvmessage(sock, &msg, sizeof(msg), recvbuf); if (ret==NETMSG_PARTIAL || ret==NETMSG_FAIL_DELIVER) { printf("partial read!\n"); // usleep(1000); continue; } if (ret!=NETMSG_SUCCESS) { fprintf(stderr, "recvmessage(): error code %d at message # %d\n", ret, i); exit(7); } printf("received message '%c' with %d bytes: %s\n", msg.hdr.id, msg.hdr.len, msg.payload.data); } /* char buf[BUFSIZE]; buf[BUFSIZE-1]='\0'; for (unsigned i=0; i<100000; i++) { int size = read(sock, buf, BUFSIZE); if (errno) { perror("read()"); exit(5); } if (size < BUFSIZE) { fprintf(stderr, "read(): received %d instead of %d bytes! (half message '%s')\n", size, BUFSIZE, buf); exit(6); } printf("received message: %s\n", buf); } */ return 0; } mmpong-0.9.1/test/perfect_bot.c0000644000175000017500000001566611131743015015413 0ustar andreandre/* Copyright (C) 2008 Steffen Basting, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "lib/message.h" #include "lib/game.h" #define PONG_KICKED (-1) #define PONG_FAILURE (-2) short volatile signal_exiting= 0; void sigterm_handler(int signal) { if (signal == SIGPIPE) printf("Caught SIGPIPE.\n"); if (signal == SIGURG) printf("Caught SIGURG.\n"); if (signal == SIGTERM) signal_exiting= 1; } int main(int argc, char *argv[]) { if (argc <= 1) { printf("Usage: %s server [port]\n", argv[0]); exit(1); } struct sigaction termact= { .sa_handler= sigterm_handler, .sa_flags= SA_RESETHAND }; sigemptyset(&termact.sa_mask); if ( (sigaction(SIGTERM, &termact, NULL)) || (termact.sa_flags= SA_RESTART, sigaction(SIGPIPE, &termact, NULL)) ) { fprintf(stderr, "Error putting signal handlers in place.\n"); exit(-1); } struct netmessage_buffer *recvbuf= NULL, *sendbuf= NULL; if ( (netmessage_buffer_init(&recvbuf) != NETMSG_SUCCESS) || (netmessage_buffer_init(&sendbuf) != NETMSG_SUCCESS) ) { fprintf(stderr, "Error initializing message buffers.\n"); exit(-1); } char *portname= "1212"; if (argc > 2) portname= argv[2]; struct addrinfo criteria= { .ai_family= AF_INET, .ai_socktype= SOCK_STREAM, .ai_protocol= IPPROTO_TCP, .ai_flags= AI_ADDRCONFIG | AI_CANONNAME // | AI_IDN | AI_CANONIDN }; struct addrinfo *peers; int stat; if ((stat= getaddrinfo(argv[1], portname, &criteria, &peers))) { fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(stat)); exit(-1); } char addrstr[INET_ADDRSTRLEN]; printf("Connecting to %s", peers->ai_canonname); if (inet_ntop(peers->ai_addr->sa_family, &((struct sockaddr_in *)(peers->ai_addr))->sin_addr, addrstr, INET_ADDRSTRLEN)) printf(" (%s:%d)", addrstr, ntohs(((struct sockaddr_in *)(peers->ai_addr))->sin_port)); printf("\n"); int sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == (-1)) { perror("socket()"); exit(-1); } if (connect(sock, peers->ai_addr, sizeof(*peers->ai_addr)) == (-1)) { perror("socket()"); exit(-1); } freeaddrinfo(peers); int yes= 1, rcvlow= 2; int maxseg= sizeof(struct netmessage); uint8_t tos= IPTOS_LOWDELAY; if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == (-1) || // we expect to receive short integers setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, &rcvlow, sizeof(rcvlow)) == (-1) || setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == (-1) || // ideally, we don't send or receive more data than a netmessage contains setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &maxseg, sizeof(maxseg)) == (-1) || setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == (-1) ) fprintf(stderr, "Socket options have not been set properly.\n"); printf("Connected.\n"); struct gameplay *game= NULL; // The reason we do this is to have the game state managed exclusively by the lib if ((game= gameplay_create()) == NULL) { fprintf(stderr, "Cannot allocate game state.\n"); exit(PONG_FAILURE); } // Try setting sensible default values if (gameplay_init(game, game->version, gamemode_linear, gamepadprofile_flat) != PONG_SUCCESS) fprintf(stderr, "Warning: Init to default game state unsuccessful.\n"); uint16_t team= 0; float pos= 0.5; struct netmessage msg; int netcode; int condition= PONG_SUCCESS; // make sure to retrieve a full game state from the server when joining the game if ( (netcode= netmessage_recv(sock, &msg, sizeof(msg), recvbuf)) == NETMSG_SUCCESS ) { switch (msg.hdr.id) { case NETMSG_STAT: gameplay_apply_state(&msg, game, &team); break; case NETMSG_KICK: printf("Got kicked (%s).\n", msg.payload.data); condition= PONG_KICKED; break; default: fprintf(stderr, "Unknown message, id == %d ('%c')\n", msg.hdr.id, (msg.hdr.id >' ')? msg.hdr.id : ' '); condition= PONG_FAILURE; break; } } else { fprintf(stderr, "Cannot determine initial game state (code == %d)\n", netcode); condition= PONG_FAILURE; } if (condition != PONG_SUCCESS) { free(game); exit(condition); } printf("I'm on team %1d\n", team); if (team > 1) team= 1; if (fcntl(sock, F_SETFL, O_NONBLOCK) == (-1)) fprintf(stderr, "Socket I/O is blocking.\n"); unsigned scoresense[2]= { 0, 0 }; while(!signal_exiting) { //handle incoming messages while ( (netcode = netmessage_recv(sock, &msg, sizeof(msg), recvbuf)) == NETMSG_SUCCESS) { switch (msg.hdr.id) { case NETMSG_STAT: case NETMSG_UPDT: gameplay_apply_state(&msg, game, &team); if (team >1) team= 1; if (msg.hdr.id == NETMSG_STAT) if (scoresense[0] != game->pad_attr[0].score || scoresense[1] != game->pad_attr[1].score) { scoresense[0]= game->pad_attr[0].score; scoresense[1]= game->pad_attr[1].score; printf("The score is now %d : %d\n",scoresense[0], scoresense[1]); } break; case NETMSG_KICK: printf("Got kicked (%s)\n", msg.payload.data); condition= PONG_KICKED; break; default: fprintf(stderr, "Unknown message, id == %d ('%c')\n", msg.hdr.id, (msg.hdr.id >' ')? msg.hdr.id : ' '); break; } } if ((netcode != NETMSG_SUCCESS) && (netcode != NETMSG_FAIL_DELIVER) && (netcode != NETMSG_PARTIAL)) { fprintf(stderr, "Socket failure while reading (%d).\n", netcode); condition= PONG_FAILURE; } if (condition != PONG_SUCCESS) break; if (sendbuf->len) netmessage_buffer_flush(sock, sendbuf); if (game->pad[team].mean != game->ball.pos[1]) { pos= game->ball.pos[1]; if (pos + game->pad[team].size / 2 > 1.f) pos = 1.f - game->pad[team].size / 2; if (pos - game->pad[team].size / 2 < 0.f) pos = game->pad[team].size / 2; } if (pos != game->pad[team].mean) { uint16_t sendpos = pos * PONG_RANGE_SPREAD; int sendcode = netmessage_send(sock, NETMSG_POS, &sendpos, sizeof(sendpos), sendbuf); if ( (sendcode!= NETMSG_SUCCESS) && (sendcode != NETMSG_FAIL_DELIVER) && (sendcode != NETMSG_PARTIAL)) { printf("Socket failure while sending (%d).\n", sendcode); break; } } usleep(50000); } netmessage_send(sock, NETMSG_EXIT, NULL, 0, sendbuf); close(sock); if (game) free(game); if (recvbuf) free(recvbuf); if (sendbuf) free(sendbuf); return 0; } mmpong-0.9.1/test/msgserver.c0000644000175000017500000000507611124424132015124 0ustar andreandre#include #include #include #include #include #include #include #include #include #include #include #include "message.h" #define BUFSIZE 128 #define PORT 4711 int main (int argc, char **argv) { errno=0; if (signal(SIGPIPE, SIG_IGN)==SIG_ERR) { fprintf(stderr, "signal(): could not set SIGPIPE signal\n"); exit(1); } int listen_sock = socket(AF_INET, SOCK_STREAM, 0); if (listen_sock < 0) { perror("socket()"); exit(1); } int val=1; setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT); if ( bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) ) { perror("bind()"); exit(2); } if ( listen(listen_sock, 10)) { perror("listen()"); exit(3); } while (1) { struct sockaddr_in peer; unsigned peerlen = sizeof(peer); int sock = accept(listen_sock, (struct sockaddr *) &peer, &peerlen); if (sock < 0) { perror("accept"); exit(5); } if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { perror("fcntl(NONBLOCK)"); exit(6); } char name[INET_ADDRSTRLEN+1]; inet_ntop(AF_INET, &peer.sin_addr, name, INET_ADDRSTRLEN); printf("connection to client %s established.\n", name); char buf[BUFSIZE]; memset(buf, 0, BUFSIZE); const char *str = "you've been PWND!"; memcpy(buf, str, strlen(str)+1); struct netmessage_buffer *sendbuf = NULL; if (netmessage_buffer_init(&sendbuf) != NETMSG_SUCCESS) { fprintf(stderr, "netmessage_buffer_init(): could not init buffer\n"); exit(4); } unsigned long dropped=0; for (unsigned i=0; i<100000; i++) { // usleep(50000); int ret = sendmessage(sock, NETMSG_KICK, buf, BUFSIZE, sendbuf); if (ret==NETMSG_PARTIAL) { printf("partial write!\n"); // usleep(5000); continue; } if (ret==NETMSG_FAIL_DELIVER) { dropped++; printf("dropped %d messages!\n", dropped); continue; } if (ret!=NETMSG_SUCCESS) { fprintf(stderr, "sendmessage(): error code %d\n", ret); break; } } /* for (unsigned i=0; i<100000; i++) { int size = write(sock, buf, BUFSIZE); if (errno) { perror("write()"); exit(5); } if (size < BUFSIZE) { fprintf(stderr, "write(): wrote %d instead of %d bytes!\n", size, BUFSIZE); exit(6); } } */ if (close(sock)) { perror("close"); exit(7); } printf("connection to client %s shutdown.\n", name); free(sendbuf); } return 0; } mmpong-0.9.1/Makefile.def0000644000175000017500000000421111131666722014162 0ustar andreandre#NAME: Project name #OBJDIR: Directory to put object files #CC: C/C++ compiler to be used #CFLAGS: Flags for the compiler #LD: Linker to use #LDFLAGS: Flags for linker NAME = mmpong SVN_REV = $(shell svnversion -n .) # Only client-gl uses resources at the moment (all the others default to empty string) CLIENT_GL_RES_PATH = resources\/ OBJDIR = ./build CC = gcc CXX = g++ LD = gcc # Beware: Some shell built-in echoes lack support of the -e parameter ECHO = /bin/echo RST = $(ECHO) -ne '\033[0m' FAIL = /bin/false SERVER_PKGS := CLIENT_CACA_PKGS := caca CLIENT_GL_PKGS := sdl CEGUI-OPENGL LIB_PKGS := COMMON_CXXFLAGS := $(CFLAGS) -I. -Wall -fPIC -fpic -g COMMON_CFLAGS := $(COMMON_CXXFLAGS) -std=gnu99 SERVER_CFLAGS := $(COMMON_CFLAGS) SERVER_LDLIBS := -Wl,-rpath,. SERVER_LDSTAT := -lpthread ifneq ("$(strip $(SERVER_PKGS))","") SERVER_CFLAGS := $(SERVER_CFLAGS) `pkg-config --cflags $(SERVER_PKGS)` SERVER_LDLIBS := $(SERVER_LDLIBS) `pkg-config --libs $(SERVER_PKGS)` endif CLIENT_CACA_CFLAGS := $(COMMON_CFLAGS) CLIENT_CACA_LDLIBS := -Wl,-rpath,. CLIENT_CACA_LDSTAT := ifneq ("$(strip $(CLIENT_CACA_PKGS))","") CLIENT_CACA_CFLAGS := $(CLIENT_CACA_CFLAGS) `pkg-config --cflags $(CLIENT_CACA_PKGS)` CLIENT_CACA_LDLIBS := $(CLIENT_CACA_LDLIBS) `pkg-config --libs $(CLIENT_CACA_PKGS)` endif CLIENT_GL_CFLAGS := $(COMMON_CFLAGS) CLIENT_GL_CXXFLAGS := $(COMMON_CXXFLAGS) CLIENT_GL_LDLIBS := -lGL -lGLU -lSDL_image -lSDL_mixer -Wl,-rpath,. CLIENT_GL_LDSTAT := ifneq ("$(strip $(CLIENT_GL_PKGS))","") CLIENT_GL_CFLAGS := $(CLIENT_GL_CFLAGS) `pkg-config --cflags $(CLIENT_GL_PKGS)` CLIENT_GL_CXXFLAGS := $(CLIENT_GL_CXXFLAGS) `pkg-config --cflags $(CLIENT_GL_PKGS)` CLIENT_GL_LDLIBS := $(CLIENT_GL_LDLIBS) `pkg-config --libs $(CLIENT_GL_PKGS)` endif LIB_CFLAGS := $(COMMON_CFLAGS) LIB_LDLIBS := -lm LIB_LDSTAT := ifneq ("$(strip $(LIB_PKGS))","") LIB_CFLAGS := $(LIB_CFLAGS) `pkg-config --cflags $(LIB_PKGS)` LIB_LDLIBS := $(LIB_LDLIBS) `pkg-config --libs $(LIB_PKGS)` endif ALL_CFLAGS := $(LIB_CFLAGS) $(SERVER_CFLAGS) $(CLIENT_GL_CFLAGS) $(CLIENT_CACA_CFLAGS) ALL_CXXFLAGS := $(LIB_CXXFLAGS) $(SERVER_CXXFLAGS) $(CLIENT_GL_CXXFLAGS) $(CLIENT_CACA_CXXFLAGS) mmpong-0.9.1/CMakeLists.txt0000644000175000017500000000265111132651464014531 0ustar andreandreCMAKE_MINIMUM_REQUIRED(VERSION 2.4) IF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) CMAKE_POLICY(SET CMP0003 NEW) CMAKE_POLICY(SET CMP0005 NEW) CMAKE_POLICY(SET CMP0006 NEW) #MAC OSX BUNDLE ENDIF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) PROJECT(mmpong) FIND_PACKAGE(Subversion) IF(Subversion_FOUND AND EXISTS .svn) Subversion_WC_INFO( ${CMAKE_SOURCE_DIR} MMPONG ) MESSAGE(STATUS "Compiling SVN revision ${MMPONG_WC_REVISION}") ENDIF(Subversion_FOUND AND EXISTS .svn) IF(CMAKE_SYSTEM_NAME STREQUAL "Windows") MESSAGE(STATUS "Compiling for capitalist Windows OS") ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Windows") STRING(COMPARE EQUAL "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}" INSOURCE) IF(INSOURCE) MESSAGE(FATAL_ERROR "Please do an out-of-source build:\ncd build\ncmake ..\nmake\n\nDon't forget to remove CMakeFiles and CMakeCache.txt before performing an out-of-source build!\n\n") ENDIF(INSOURCE) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}) SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}) SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) SET(INC_DIR_LIB ${CMAKE_SOURCE_DIR}/lib) ADD_SUBDIRECTORY(lib) ADD_SUBDIRECTORY(server) ADD_SUBDIRECTORY(client_caca) ADD_SUBDIRECTORY(client_gl) mmpong-0.9.1/server/0000755000175000017500000000000011354734770013303 5ustar andreandremmpong-0.9.1/server/thread.h0000644000175000017500000000140211122173560014703 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __THREAD_HEADER__ #define __THREAD_HEADER__ int worker_thread(const void *); #endif mmpong-0.9.1/server/addrtable.h0000644000175000017500000000334511124135235015365 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __ADDRTABLE_HEADER__ #define __ADDRTABLE_HEADER__ #include #include #define LOOKUP_TABLE_SIZE 1031 // Quick drive-by instructions on how to use this address table: // * create and initialize the global instance // * acquire thread-safe access to the value associated with an element key by using addrtable_atomic // * functions to provide: // -- int operation(unsigned *value); // this one is supposed to perform the operations requested on `value`; return codes are handed back to the caller; note though: negative return values are reserved for exclusive use by `addrtable_atomic` itself // -- unsigned preset(const struct sockaddr_in key); // this one is supposed to initialize a newly created key with a default value by returning the desired value int addrtable_init(const int, const pthread_mutexattr_t *); void addrtable_destroy(const short); int addrtable_is_active(void); int addrtable_remove(const struct sockaddr_in); int addrtable_atomic(const struct sockaddr_in, int (*)(unsigned *), unsigned (*)(const struct sockaddr_in)); #endif mmpong-0.9.1/server/config.h.in0000644000175000017500000000150111131661577015320 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __VERSION_H__ #define __VERSION_H__ #define VER_MAJ 0 #define VER_MIN 9 #define VER_SVN "${SVN_REV}" #define RESPATH "${SERVER_RES_PATH}" #endif mmpong-0.9.1/server/addrtable.c0000644000175000017500000001577011124140202015353 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include "addrtable.h" #include "hashmap.h" #include "linklist.h" #include "sizeof.h" #define FAST_ADDRTABLE (( __UNSIGNED_SIZE__ == __VOID_PTR_SIZE__) && (__UNSIGNED_SIZE__ == __IN_ADDR_T_SIZE__)) static struct hashmap *addrtable= NULL; static struct linklist *addrlist= NULL, *addrtail= NULL; static pthread_mutex_t atabsync; static unsigned addrtable_map(const unsigned, const void *); static int addrtable_cmp(const void *, const void *); #if !FAST_ADDRTABLE static int prep_addrtail(const struct sockaddr_in); #endif static void free_wrapper(void *); int addrtable_init(max_connect, mutexattr) const int max_connect; const pthread_mutexattr_t *mutexattr; { if (addrtable) return (-1); // prepare address lookup table for flood protection unsigned mapsize= LOOKUP_TABLE_SIZE; if (max_connect >0) mapsize= max_connect /2; addrtable= hashmap_create(mapsize, NULL, NULL); if (addrtable) { if (pthread_mutex_init(&atabsync, mutexattr)) { fprintf(stderr, "Error creating synchronization primitive, dropping flood protection.\n"); hashmap_destroy(addrtable); addrtable= NULL; } } return (addrtable == NULL) * (-1); } void addrtable_destroy(hard) const short hard; { if (pthread_mutex_lock(&atabsync)) fprintf(stderr, "Error syncing on mutex.\n"); if (addrtable) { hashmap_destroy(addrtable); addrtable= NULL; } if (addrlist) { linklist_destroy(addrlist, free_wrapper); addrlist= addrtail= NULL; } if (pthread_mutex_unlock(&atabsync)) fprintf(stderr, "Error releasing mutex.\n"); if (hard && pthread_mutex_destroy(&atabsync)) fprintf(stderr, "Error destroying mutex object.\n"); } int addrtable_is_active(void) // new compilers can `inline` this { return (addrtable != NULL); } #if FAST_ADDRTABLE // fast and elegant static unsigned addrtable_map(size, key) const unsigned size; const void *key; { return ((unsigned)key) %size; } static int addrtable_cmp(ikey, skey) const void *ikey, *skey; { return ((unsigned)ikey) != ((unsigned)skey); } #else // slower, but generic static unsigned addrtable_map(size, key) const unsigned size; const void *key; { /* struct sockaddr_in sample; memcpy(&sample.sin_addr.s_addr, key, sizeof(sample.sin_addr.s_addr)); return ((unsigned) sample.sin_addr.s_addr)%size; */ return *((unsigned *)key) %size; } static int addrtable_cmp(ikey, skey) const void *ikey, *skey; { struct sockaddr_in sample; return memcmp(ikey, skey, sizeof(sample.sin_addr.s_addr)); } static int prep_addrtail(peer) const struct sockaddr_in peer; { if (!addrtail) { if (!addrlist) addrlist= calloc(1, sizeof(struct linklist)); addrtail= addrlist; if (addrtail) // forward to the end while (addrtail->next) addrtail= addrtail->next; } if (!addrtail) return (-1); if (!addrtail->data) addrtail->data= malloc(sizeof(peer.sin_addr.s_addr)); if (!addrtail->data) return (-1); memcpy(addrtail->data, &peer.sin_addr.s_addr, sizeof(peer.sin_addr.s_addr)); return 0; } #endif // just to ensure libc independence static void free_wrapper(data) void *data; { if (data) free(data); } int addrtable_remove(peer) const struct sockaddr_in peer; { // guard if (!addrtable_is_active()) return (-1); if (pthread_mutex_lock(&atabsync)) { fprintf(stderr, "Error syncing on mutex.\n"); return (-2); } if (!addrtable_is_active()) { if (pthread_mutex_unlock(&atabsync)) fprintf(stderr, "Error releasing mutex.\n"); return (-1); } int retval= (-1); #if FAST_ADDRTABLE // fast, but highly dependent on the architecture/protocol used retval= hashmap_remove(addrtable, (void *)(peer.sin_addr.s_addr), addrtable_map, addrtable_cmp); #else // independent of arch/proto retval= hashmap_remove(addrtable, &peer.sin_addr.s_addr, addrtable_map, addrtable_cmp); if (!retval) { // key has been added struct linklist *this, *prev= NULL; for (this= addrlist; this; prev= this, this= this->next) { if (!this->data) continue; if (!addrtable_cmp(this->data, &peer.sin_addr.s_addr)) { // if (prev == this) prev= NULL; if (prev) prev->next= this->next; else addrlist= prev= this->next; if (this->data) free(this->data); free(this); // this= prev; break; } } } #endif if (pthread_mutex_unlock(&atabsync)) fprintf(stderr, "Error releasing mutex.\n"); return retval; } // return values: // (>=0) == success (return value is operation-specific) // ( -1) == table uninitialized // ( -2) == error acquiring mutex // ( -3) == error adding list element int addrtable_atomic(peer, operation, preset) const struct sockaddr_in peer; int (*operation)(unsigned *); unsigned (*preset)(const struct sockaddr_in); { // guard if (!addrtable_is_active()) return (-1); if (pthread_mutex_lock(&atabsync)) { fprintf(stderr, "Error syncing on mutex.\n"); return (-2); } int retval= (-1); if (!addrtable_is_active()) goto leave; unsigned *value= NULL; retval= 0; #if FAST_ADDRTABLE // fast, but highly dependent on the architecture/protocol used unsigned preval= 0; value= (unsigned *)hashmap_lookup(addrtable, (void *)(peer.sin_addr.s_addr), addrtable_map, addrtable_cmp); #else // independent of arch/proto void *prevalgeneric= NULL; if (!prep_addrtail(peer)) value= (unsigned *)hashmap_lookup(addrtable, addrtail->data, addrtable_map, addrtable_cmp); #endif if (value == NULL) { #if FAST_ADDRTABLE // fast, but highly dependent on the architecture/protocol used if (preset) preval= preset(peer); value= &preval; #else // independent of arch/proto if (preset) prevalgeneric= ((void *)NULL) + preset(peer); value= (unsigned *)&prevalgeneric; #endif } int passthru= operation(value); if (passthru < 0) passthru= 0; #if FAST_ADDRTABLE if (value == &preval) retval= hashmap_insert(addrtable, (void *)(peer.sin_addr.s_addr), (void *)preval, addrtable_map, addrtable_cmp); #else if (value == (unsigned *)&prevalgeneric) { retval= (-3); if (prep_addrtail(peer)) { fprintf(stderr, "Error dealing with a linked list.\n"); goto leave; } retval= hashmap_insert(addrtable, addrtail->data, prevalgeneric, addrtable_map, addrtable_cmp); if (!retval) { // key has been added addrtail->next= calloc(1, sizeof(struct linklist)); addrtail= addrtail->next; if (!addrtail) retval= (-3); } } #endif if (!retval) retval= passthru; leave: if (pthread_mutex_unlock(&atabsync)) fprintf(stderr, "Error releasing mutex.\n"); return retval; } mmpong-0.9.1/server/hashmap.h0000644000175000017500000000420411124134714015060 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __HASHMAP_HEADER__ #define __HASHMAP_HEADER__ // Quick drive-by instructions on how to use this mapping scheme: // * create map of desired size (I will spare you the introduction to stochastics here) // * after that, you can begin to insert (key, value) pairs into the map, look them up, work on the values in store and remove them easily // * functions to provide: // -- int map(int hashmap_size, void *key); // this one is supposed to map any given key to a number in [ 0 ; hashmap_size-1 ] // -- int cmp(void *key_to_be_found, void *key_in_store); // this one needs to recognize a key when iterating through possible candidates (e.g. strcmp, memcmp) struct hashentry { void **keys; void **vals; unsigned size; }; struct hashmap { struct hashentry *table; unsigned size; }; struct hashmap *hashmap_create(unsigned, const struct hashmap *, unsigned (*)(const unsigned, const void *)); void hashmap_destroy(struct hashmap *); void **hashmap_lookup(struct hashmap *, const void *, unsigned (*)(const unsigned, const void *), int (*)(const void *, const void *)); int hashmap_insert(struct hashmap *, void *, void *, unsigned (*)(const unsigned, const void *), int (*)(const void *, const void *)); int hashmap_remove(struct hashmap *, const void *, unsigned (*)(const unsigned, const void *), int (*)(const void *, const void *)); //unsigned hashmap_fast_inc(struct hashmap *, unsigned, int, unsigned (*)(unsigned, void *)); // special operation to speed up server accesses #endif mmpong-0.9.1/server/hashmap.c0000644000175000017500000001311011124134714015047 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include "hashmap.h" static unsigned findPrime(number) unsigned number; // find the next best prime number starting from `number` { unsigned check= number >> 1, iter; while (check*check > number) check>>= 1; // rough int sqrt approximation for (;;number++) { if (check*check < number) check++; for (iter= 2; iter< check; iter++) if ((number % iter) == 0) break; if (iter >= check) return number; } return number; } struct hashmap *hashmap_create(size, rehash, map) unsigned size; const struct hashmap *rehash; // copy elements from the optional `rehash` map into the new one (can be NULL) unsigned (*map)(const unsigned, const void *); { if (size< 2) size= 2; size= findPrime(size -2); // actual hashmap size struct hashmap *hash= malloc(sizeof(struct hashmap)); if (!hash) return NULL; hash->size= size; hash->table= calloc(size, sizeof(struct hashentry)); if (!hash->table) { free(hash); return NULL; } if ((!rehash)||(!map)) return hash; for (int idx= 0; idx< rehash->size; idx++) { for (int ent= 0; ent< rehash->table[idx].size; ent++) { struct hashentry *dest= hash->table + (map(size, rehash->table[idx].keys[ent]) %size); dest->keys= realloc(dest->keys, (dest->size +1)*sizeof(void *)); dest->vals= realloc(dest->vals, (dest->size +1)*sizeof(void *)); if ((!dest->keys) || (!dest->vals)) { hashmap_destroy(hash); return NULL; } dest->keys[dest->size]= rehash->table[idx].keys[ent]; dest->vals[dest->size]= rehash->table[idx].vals[ent]; dest->size++; } } return hash; } void hashmap_destroy(hash) struct hashmap *hash; { if (!hash) return; if (hash->table) { for (int idx= 0; idx< hash->size; idx++) { if (hash->table[idx].keys) free(hash->table[idx].keys); if (hash->table[idx].vals) free(hash->table[idx].vals); } free(hash->table); } free(hash); } void **hashmap_lookup(hash, key, map, cmp) struct hashmap *hash; const void *key; unsigned (*map)(const unsigned, const void *); int (*cmp)(const void *, const void *); { if ((!hash)||(!map)||(!cmp)) return NULL; struct hashentry *dest= hash->table + (map(hash->size, key) %hash->size); for (int idx= 0; idx< dest->size; idx++) if (!cmp(key, dest->keys[idx])) return dest->vals+idx; return NULL; } // overwrites key if present before // return values: <0: fatal, ==0: regular, >0: overwrite int hashmap_insert(hash, key, val, map, cmp) struct hashmap *hash; void *key, *val; unsigned (*map)(const unsigned, const void *); int (*cmp)(const void *, const void *); { if ((!hash)||(!map)||(!cmp)) return (-1); struct hashentry *dest= hash->table + (map(hash->size, key) %hash->size); for (int idx= 0; idx< dest->size; idx++) if (!cmp(key, dest->keys[idx])) { dest->vals[idx]= val; return 1; } dest->keys= realloc(dest->keys, (dest->size +1)*sizeof(void *)); dest->vals= realloc(dest->vals, (dest->size +1)*sizeof(void *)); if ((!dest->keys) || (!dest->vals)) { if (dest->keys) free(dest->keys); if (dest->vals) free(dest->vals); dest->keys= dest->vals= NULL; dest->size= 0; return (-2); } dest->keys[dest->size]= key; dest->vals[dest->size]= val; dest->size++; return 0; } // return values: <0: fatal, ==0: regular, >0: not found int hashmap_remove(hash, key, map, cmp) struct hashmap *hash; const void *key; unsigned (*map)(const unsigned, const void *); int (*cmp)(const void *, const void *); { if ((!hash)||(!map)||(!cmp)) return (-1); struct hashentry *dest= hash->table + (map(hash->size, key) %hash->size); for (int idx= 0; idx< dest->size; idx++) { if (cmp(key, dest->keys[idx])) continue; memmove(dest->keys+idx, dest->keys+idx+1, (dest->size -idx -1)*sizeof(void *)); memmove(dest->vals+idx, dest->vals+idx+1, (dest->size -idx -1)*sizeof(void *)); dest->size--; dest->keys= realloc(dest->keys, (dest->size)*sizeof(void *)); dest->vals= realloc(dest->vals, (dest->size)*sizeof(void *)); if ((!dest->keys) || (!dest->vals)) { if (dest->keys) free(dest->keys); if (dest->vals) free(dest->vals); dest->keys= dest->vals= NULL; dest->size= 0; return (-2); } return 0; } return 1; } /* unsigned hashmap_fast_inc(hash, key, offset, map) struct hashmap *hash; unsigned key; int offset; unsigned (*map)(unsigned, void *); { if ((!hash)||(!map)) return (-1); struct hashentry *dest= hash->table + (map(hash->size, (void *)key) %hash->size); for (int idx= 0; idx< dest->size; idx++) if (key == (unsigned) dest->keys[idx]) { dest->vals[idx]= (void *)( ((unsigned) dest->vals[idx]) + offset ); return dest->vals[idx]; } dest->keys= realloc(dest->keys, (dest->size +1)*sizeof(void *)); dest->vals= realloc(dest->vals, (dest->size +1)*sizeof(void *)); if ((!dest->keys) || (!dest->vals)) { if (dest->keys) free(dest->keys); if (dest->vals) free(dest->vals); dest->keys= dest->vals= NULL; dest->size= 0; return 0; } dest->keys[dest->size]= (void *)key; dest->vals[dest->size]= (void *)offset; dest->size++; return offset; } */ mmpong-0.9.1/server/sizeof.h.in0000644000175000017500000000145211131677005015350 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #define __UNSIGNED_SIZE__ ${UNSIGNED_SIZE} #define __VOID_PTR_SIZE__ ${VOID_PTR_SIZE} #define __IN_ADDR_T_SIZE__ ${IN_ADDR_T_SIZE} mmpong-0.9.1/server/CMakeLists.txt0000644000175000017500000000170711132703607016035 0ustar andreandreSET( SERVER_SOURCES addrtable.c hashmap.c linklist.c main.c thread.c ) SET( SVN_REV "${MMPONG_WC_REVISION}" ) FIND_PACKAGE(Threads) IF(CMAKE_USE_PTHREADS_INIT) MESSAGE(STATUS "found pthreads: enabling server.") SET( CONFIGURE_DIR "${CMAKE_CURRENT_BINARY_DIR}") CONFIGURE_FILE ( config.h.in ${CONFIGURE_DIR}/config.h ) INCLUDE(CheckTypeSize) SET(CMAKE_EXTRA_INCLUDE_FILES "netinet/in.h") CHECK_TYPE_SIZE( "void *" VOID_PTR_SIZE ) CHECK_TYPE_SIZE( "unsigned" UNSIGNED_SIZE ) CHECK_TYPE_SIZE( "in_addr_t" IN_ADDR_T_SIZE ) CONFIGURE_FILE ( sizeof.h.in ${CONFIGURE_DIR}/sizeof.h ) ADD_EXECUTABLE( mmpongd ${SERVER_SOURCES} ) INCLUDE_DIRECTORIES( mmpongd ${INC_DIR_LIB} ${CMAKE_SOURCE_DIR} ${CONFIGURE_DIR}) TARGET_LINK_LIBRARIES( mmpongd libmmpong ${CMAKE_THREAD_LIBS_INIT}) INSTALL(TARGETS mmpongd RUNTIME DESTINATION games) ELSE(CMAKE_USE_PTHREADS_INIT) MESSAGE(STATUS "could not find pthreads: disabling server.") ENDIF(CMAKE_USE_PTHREADS_INIT) mmpong-0.9.1/server/thread.c0000644000175000017500000004724111132734433014714 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include #include #include #include #include "thread.h" #include "addrtable.h" #include "lib/game.h" #include "lib/message.h" #define SMALL_TEAM 10 #define NOWAIT_INTERVAL 10 #define FLAG_REFRESH_STATE 0x01 #define FLAG_ALLOW_RTTM 0x02 struct thread_state { int *sockets; in_addr_t *peers; struct netmessage_buffer **netrecvbuf, **netsendbuf; float *positions, *diffs; char *flags; short *teams; long *team_count; unsigned thread_connects; }; // helps get the realloc/memmove hell under control (consider unifying some of these macros with the ones in lib/message.c) #define OFFSET(base, member) ((int)( ((void *)&(member)) - ((void *)&(base)) )) #define INSTANCE(var, offset) *((void **)( ((void *)(var)) + (offset) )) #define REGISTER(member) { OFFSET(thread_state_sample, thread_state_sample.member), sizeof(*thread_state_sample.member) } static struct thread_state thread_state_sample; static const struct { const int offset; const int membsize; } thread_state_members[]= { REGISTER(sockets), REGISTER(netrecvbuf), REGISTER(netsendbuf), REGISTER(positions), REGISTER(diffs), REGISTER(flags), REGISTER(teams), { 0, 0 } }; // communicate with master thread extern short volatile signal_exiting; extern unsigned volatile bcast_interval; extern unsigned volatile rttm_min; extern struct gameplay_public volatile game; extern pthread_rwlock_t gsync; extern unsigned *n_connects; extern int * volatile insert_socket; extern in_addr_t * volatile insert_peer; extern pthread_mutex_t *isync; extern float *sums; extern float *variances; extern pthread_rwlock_t statsync; extern short volatile n_teams; extern short volatile verbosity; static int add_client(const int, int, in_addr_t, struct thread_state *); static int remove_client(const int, const int, struct thread_state *); static short choose_team(const short, const long *); static inline int compensate_timer(const unsigned, const struct timeval *, struct timeval *); static inline int rttm_timer(const unsigned, const struct timeval *, struct timeval *); static int send_status_full(const int, struct netmessage_buffer *, const struct gameplay_public *, const short); static int send_status_update(const int, struct netmessage_buffer *, const struct gameplay_public *); static int peer_cnt_decrease(unsigned *); int worker_thread(_tlidx) const void * _tlidx; { const int tlidx=(int *)_tlidx - insert_socket; struct thread_state tstate= { .sockets= NULL, .peers= NULL, .positions= NULL, .diffs= NULL, .flags= NULL, .teams= NULL, .netsendbuf= NULL, .netrecvbuf= NULL, .team_count= calloc(n_teams, sizeof(long)), .thread_connects= 0 }; unsigned *scoresense= calloc(1, sizeof(unsigned) * n_teams); assert(tstate.team_count && scoresense); for (int idx= 0; idx< n_teams; idx++) // resume where we might have left off tstate.thread_connects+= n_connects[tlidx * n_teams + idx]; struct timeval ratesync, lastrttm; gettimeofday(&ratesync, NULL); gettimeofday(&lastrttm, NULL); short gamerefresh= 1; int flushstart= 0, ratereset= 1; while (!signal_exiting) { // check for queued up connections if (insert_socket[tlidx]) { int hold= (-1); in_addr_t peer=0; // hand over if (pthread_mutex_lock(isync+tlidx)) fprintf(stderr, "Worker Thread [%d]: Cannot acquire mutex lock.\n", tlidx); else { hold= insert_socket[tlidx]; if (insert_peer) peer= insert_peer[tlidx]; insert_socket[tlidx]= 0; if (pthread_mutex_unlock(isync+tlidx)) fprintf(stderr, "Worker Thread [%d]: Cannot release mutex lock.\n", tlidx); } // accept if (hold != (-1)) add_client(tlidx, hold, peer, &tstate); // the initial game state is flagged to be transmitted here } fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); int nfds= 0; for (int idx= 0; idx< tstate.thread_connects; idx++) { FD_SET(tstate.sockets[idx], &rfds); if (nfds <= tstate.sockets[idx]) nfds= tstate.sockets[idx] +1; if (tstate.netsendbuf[idx]->pos < tstate.netsendbuf[idx]->len) FD_SET(tstate.sockets[idx], &wfds); } // set bcast timer struct timeval period; if (ratereset) { gettimeofday(&period, NULL); compensate_timer(bcast_interval, &ratesync, &period); ratereset= 0; } int retselect= select(nfds, &rfds, &wfds, NULL, &period); gettimeofday(&ratesync, NULL); // used to sync with desired notifier rate if (retselect == (-1)) { perror("Thread: select()"); continue; } if (rttm_timer(rttm_min, &ratesync, &lastrttm)) { for (int idx= 0; idx< tstate.thread_connects; idx++) tstate.flags[idx]|= FLAG_ALLOW_RTTM; } // flush out pending messages when client sockets become available short setflush= 0; for (int cnt= 0; cnt< tstate.thread_connects; cnt++) { int idx= (flushstart + cnt) % tstate.thread_connects; if (FD_ISSET(tstate.sockets[idx], &wfds)) if (netmessage_buffer_flush(tstate.sockets[idx], tstate.netsendbuf[idx]) == NETMSG_FAIL_DELIVER) if (!setflush) { flushstart= idx; // round-robin setflush= 1; } } // inform connected clients of the current game status periodically if ((1000L * 1000L) * (long)period.tv_sec + period.tv_usec <= NOWAIT_INTERVAL) { ratereset= 1; if (pthread_rwlock_rdlock(&gsync)) { fprintf(stderr, "Worker Thread [%d]: Cannot acquire read lock.\n", tlidx); } else { struct gameplay_public gamelocal; memcpy(&gamelocal, (void *)&game, sizeof(gamelocal)); if (pthread_rwlock_unlock(&gsync)) fprintf(stderr, "Worker Thread [%d]: Cannot release read lock.\n", tlidx); // broadcast update messages if (!gamerefresh) gamerefresh= (gamelocal.status != gamestatus_running); if (!gamerefresh) { // determine whether full state messages need to be sent for (int idx= 0; idx< n_teams; idx++) if (scoresense[idx] != gamelocal.pad_attr[idx].score) { gamerefresh= 1; scoresense[idx]= gamelocal.pad_attr[idx].score; } } if (gamerefresh) for (int idx= 0; idx< tstate.thread_connects; idx++) tstate.flags[idx]|= FLAG_REFRESH_STATE; for (int idx= 0; idx< tstate.thread_connects; idx++) { // printf("Worker Thread [%d]: Updating client [%d].\n", tlidx, tstate.sockets[idx]); int sendcode; if (tstate.flags[idx] & FLAG_REFRESH_STATE) { // keep sending full state messages until the next game starts if (gamelocal.status == gamestatus_running) tstate.flags[idx]&= ~FLAG_REFRESH_STATE; if ( ((sendcode= send_status_full( tstate.sockets[idx], tstate.netsendbuf[idx], &gamelocal, tstate.teams[idx])) != NETMSG_SUCCESS) && (sendcode != NETMSG_PARTIAL) ) fprintf(stderr, "Worker Thread [%d]: Cannot send status update.\n", tlidx); } // incremental update else if ( ((sendcode= send_status_update(tstate.sockets[idx], tstate.netsendbuf[idx], &gamelocal)) != NETMSG_SUCCESS) && (sendcode != NETMSG_PARTIAL) ) fprintf(stderr, "Worker Thread [%d]: Cannot bring client up to speed.\n", tlidx); if (sendcode == NETMSG_FAIL_SOCKET) { close(tstate.sockets[idx]); fprintf(stderr, "NETMSG_FAIL_SOCKET from socket.\n"); remove_client(tlidx, idx, &tstate); idx--; } // printf("Worker Thread [%d]: Finished updating client [%d].\n", tlidx, tstate.sockets[idx]); } } } // receive and process messages from clients for (int idx= 0; idx< tstate.thread_connects; idx++) if (FD_ISSET(tstate.sockets[idx], &rfds)) { struct netmessage msg; int recvcode; // printf("Worker Thread [%d]: Processing client [%d].\n", tlidx, tstate.sockets[idx]); // process incoming messages float padpos= 0.5; short padupdate= 0; while ((recvcode= netmessage_recv(tstate.sockets[idx], &msg, sizeof(msg), tstate.netrecvbuf[idx])) == NETMSG_SUCCESS) { if (msg.hdr.id == NETMSG_EXIT) { close(tstate.sockets[idx]); fprintf(stderr, "NETMSG_EXIT from socket.\n"); remove_client(tlidx, idx, &tstate); idx--; break; } if (msg.hdr.id != NETMSG_POS) continue; // use values outside the valid range for RTTM purposes if (msg.payload.position > PONG_RANGE_SPREAD) { if ((tstate.flags[idx] & FLAG_ALLOW_RTTM) == 0) continue; netmessage_send(tstate.sockets[idx], NETMSG_POS, &msg.payload.position, sizeof(msg.payload.position), tstate.netsendbuf[idx]); if (rttm_min >0) tstate.flags[idx]&= ~FLAG_ALLOW_RTTM; continue; } // regular client message processing padpos= ((float)msg.payload.position) / PONG_RANGE_SPREAD; padupdate++; } // drop client on serious conditions if ( (recvcode == NETMSG_FAIL_SOCKET) || (recvcode == NETMSG_FAIL_CHECKSUM) || (recvcode == NETMSG_END_SOCKET) ) { if (recvcode != NETMSG_END_SOCKET) { char *msg= "Socket fault"; netmessage_send(tstate.sockets[idx], NETMSG_KICK, msg, strlen(msg) +1, tstate.netsendbuf[idx]); fprintf(stderr, "Socket fault (%s).\n", (recvcode == NETMSG_FAIL_SOCKET)? "NETMSG_FAIL_SOCKET":"NETMSG_FAIL_CHECKSUM"); } close(tstate.sockets[idx]); remove_client(tlidx, idx, &tstate); idx--; padupdate= 0; } // propagate updated position if (padupdate) { if (pthread_rwlock_rdlock(&gsync)) fprintf(stderr, "Worker Thread [%d]: Cannot acquire read lock.\n", tlidx); float teammean= ((float)(game.pad[ tstate.teams[idx] ].mean)) / PONG_RANGE_SPREAD; // use most current values unsigned teampeers= game.pad_attr[ tstate.teams[idx] ].peers; if (pthread_rwlock_unlock(&gsync)) fprintf(stderr, "Worker Thread [%d]: Cannot release read lock.\n", tlidx); // semantics on this lock may seem strange, but make perfect sense, since write operations are deliberately designed to be non-overlapping if (pthread_rwlock_rdlock(&statsync)) { fprintf(stderr, "Worker Thread [%d]: Cannot acquire write lock.\n", tlidx); } else { // publish updated position sums[tlidx * n_teams + tstate.teams[idx]]+= padpos -tstate.positions[idx]; float unitdiff= teammean - padpos; if (teampeers >0) unitdiff+= (padpos - tstate.positions[idx]) / teampeers; // improves accuracy somewhat variances[tlidx * n_teams + tstate.teams[idx]]+= (unitdiff * unitdiff) -tstate.diffs[idx]; if (pthread_rwlock_unlock(&statsync)) fprintf(stderr, "Worker Thread [%d]: Cannot release write lock.\n", tlidx); tstate.positions[idx]= padpos; tstate.diffs[idx]= unitdiff * unitdiff; } } // printf("Worker Thread [%d]: Finished processing client [%d].\n", tlidx, tstate.sockets[idx]); } } // clean up for (int idx= 0; idx< tstate.thread_connects; idx++) { char *msg= "Shutdown"; netmessage_send(tstate.sockets[idx], NETMSG_KICK, msg, strlen(msg) +1, tstate.netsendbuf[idx]); close(tstate.sockets[idx]); } if (tstate.netrecvbuf) for (int idx= 0; idx< tstate.thread_connects; idx++) free(tstate.netrecvbuf[idx]); if (tstate.netsendbuf) for (int idx= 0; idx< tstate.thread_connects; idx++) free(tstate.netsendbuf[idx]); // semantics on this lock may seem strange, but make perfect sense, since write operations are non-overlapping if (pthread_rwlock_rdlock(&statsync)) fprintf(stderr, "Worker Thread [%d]: Cannot acquire write lock.\n", tlidx); for (int idx= 0; idx< n_teams; idx++) { sums[tlidx * n_teams + idx]= variances[tlidx * n_teams + idx]= n_connects[tlidx * n_teams + idx]= 0; } if (pthread_rwlock_unlock(&statsync)) fprintf(stderr, "Worker Thread [%d]: Cannot release write lock.\n", tlidx); // free remaining resources for (int idx= 0; thread_state_members[idx].membsize; idx++) { if (INSTANCE(&tstate, thread_state_members[idx].offset)) free(INSTANCE(&tstate, thread_state_members[idx].offset)); INSTANCE(&tstate, thread_state_members[idx].offset)= NULL; } if (tstate.team_count) free(tstate.team_count); if (insert_peer) free(tstate.peers); if (scoresense) free(scoresense); //pthread_exit((void *)0); return 0; } static int add_client(tlidx, newsock, newpeer, tstate) const int tlidx; int newsock; in_addr_t newpeer; struct thread_state *tstate; { if (verbosity) printf("Worker Thread [%d]: Adding client [%d].\n", tlidx, newsock); for (int idx= 0; thread_state_members[idx].membsize; idx++) { INSTANCE(tstate, thread_state_members[idx].offset)= realloc(INSTANCE(tstate, thread_state_members[idx].offset), thread_state_members[idx].membsize * (tstate->thread_connects +1)); assert(INSTANCE(tstate, thread_state_members[idx].offset)); } if (insert_peer) { tstate->peers= realloc(tstate->peers, sizeof(in_addr_t) * (tstate->thread_connects +1)); assert(tstate->peers); } tstate->sockets[tstate->thread_connects]= newsock; if (insert_peer) tstate->peers[tstate->thread_connects]= newpeer; tstate->netsendbuf[tstate->thread_connects]= tstate->netrecvbuf[tstate->thread_connects]= NULL; netmessage_buffer_init( tstate->netrecvbuf + tstate->thread_connects ); netmessage_buffer_init( tstate->netsendbuf + tstate->thread_connects ); tstate->positions[tstate->thread_connects]= .5; tstate->teams[tstate->thread_connects]= choose_team(n_teams, tstate->team_count); tstate->flags[tstate->thread_connects]= FLAG_REFRESH_STATE | FLAG_ALLOW_RTTM; // semantics on this lock may seem strange, but make perfect sense, since write operations are non-overlapping if (pthread_rwlock_rdlock(&statsync)) { close(newsock); fprintf(stderr, "Worker Thread [%d]: Cannot acquire shared write lock.\n", tlidx); return (-1); } else { unsigned teampeers= game.pad_attr[ tstate->teams[tstate->thread_connects] ].peers; float teammean= ( ( ((float)(game.pad[ tstate->teams[tstate->thread_connects] ].mean)) /PONG_RANGE_SPREAD ) * teampeers + tstate->positions[tstate->thread_connects] ) / (teampeers + 1); // improves accuracy somewhat float unitdiff= teammean - tstate->positions[tstate->thread_connects]; tstate->diffs[tstate->thread_connects]= unitdiff * unitdiff; n_connects[ tlidx * n_teams + tstate->teams[tstate->thread_connects] ]++; sums[ tlidx * n_teams + tstate->teams[tstate->thread_connects] ]+= tstate->positions[tstate->thread_connects]; variances[ tlidx * n_teams + tstate->teams[tstate->thread_connects] ]+= tstate->diffs[tstate->thread_connects]; if (pthread_rwlock_unlock(&statsync)) fprintf(stderr, "Worker Thread [%d]: Cannot release shared write lock.\n", tlidx); tstate->team_count[ tstate->teams[tstate->thread_connects] ]++; tstate->thread_connects++; } return 0; } static int remove_client(tlidx, sockidx, tstate) const int tlidx, sockidx; struct thread_state *tstate; { if (verbosity) printf("Worker Thread [%d]: Removing client [%d].\n", tlidx, tstate->sockets[sockidx]); int retcode= 0; int ncidx= tlidx * n_teams + tstate->teams[sockidx]; // semantics on this lock may seem strange, but make perfect sense, since write operations are non-overlapping if (pthread_rwlock_rdlock(&statsync)) { fprintf(stderr, "Worker Thread [%d]: Cannot acquire shared write lock.\n", tlidx); retcode= (-1); } n_connects[ncidx]--; sums[ncidx]-= tstate->positions[sockidx]; variances[ncidx]-= tstate->diffs[sockidx]; if (!n_connects[ncidx]) sums[ncidx] = variances[ncidx] = 0; if (pthread_rwlock_unlock(&statsync)) { fprintf(stderr, "Worker Thread [%d]: Cannot release shared write lock.\n", tlidx); retcode= (-1); } tstate->thread_connects--; tstate->team_count[ tstate->teams[sockidx] ]--; if ( tstate->netrecvbuf[sockidx] ) free( tstate->netrecvbuf[sockidx] ); if ( tstate->netsendbuf[sockidx] ) free( tstate->netsendbuf[sockidx] ); if (insert_peer) { struct sockaddr_in sin; sin.sin_addr.s_addr= tstate->peers[sockidx]; memmove(tstate->peers +sockidx, tstate->peers +sockidx +1, (tstate->thread_connects -sockidx) *sizeof(in_addr_t)); tstate->peers= realloc(tstate->peers, sizeof(in_addr_t) * tstate->thread_connects); if (tstate->thread_connects) assert(tstate->peers); int val= addrtable_atomic(sin, peer_cnt_decrease, NULL); if ((val > 0) || ((-1) > val)) addrtable_remove(sin); } for (int idx= 0; thread_state_members[idx].membsize; idx++) { memmove(INSTANCE(tstate, thread_state_members[idx].offset) +sockidx *thread_state_members[idx].membsize, INSTANCE(tstate, thread_state_members[idx].offset) +(sockidx +1) *thread_state_members[idx].membsize, (tstate->thread_connects -sockidx) *thread_state_members[idx].membsize); INSTANCE(tstate, thread_state_members[idx].offset)= realloc(INSTANCE(tstate, thread_state_members[idx].offset), thread_state_members[idx].membsize * tstate->thread_connects); if (tstate->thread_connects) assert(INSTANCE(tstate, thread_state_members[idx].offset)); // else assert(!INSTANCE(tstate, thread_state_members[idx].offset)); -- this depends on the libc implementation specifics from all I know about POSIX } return retcode; } static short choose_team(n_teams, members) const short n_teams; const long *members; { // deterministic start short minteam= 0; for (short idx= 0; idx< n_teams; idx++) if ((members[idx] <= SMALL_TEAM)&&(members[idx] < members[minteam])) minteam= idx; if (members[minteam] <= SMALL_TEAM) return minteam; // probabilistic behavior for larger teams (preserve order of magnitude balance) minteam= (short)(rand() % n_teams); for (short idx= 0; idx< n_teams; idx++) { int max= 8; long magn= 1; while ((members[idx] / magn)&&(max-- > 0)) magn*= 5; if (members[minteam] / magn) minteam= idx; } return minteam; } // adjust for time slip static inline int compensate_timer(interval, sync, period) const unsigned interval; const struct timeval *sync; struct timeval *period; { long reltime= (long)interval - ( ((long)period->tv_sec) - ((long)sync->tv_sec) ) * (1000L * 1000L) - period->tv_usec + sync->tv_usec; if (reltime < NOWAIT_INTERVAL) reltime= NOWAIT_INTERVAL; period->tv_sec= reltime / (1000L * 1000L); period->tv_usec= reltime % (1000L * 1000L); return (reltime == NOWAIT_INTERVAL); } static inline int rttm_timer(interval, sync, last) const unsigned interval; const struct timeval *sync; struct timeval *last; { if (!interval) return 0; long reltime= ( ((long)sync->tv_sec) - ((long)last->tv_sec) ) * 1000L + (sync->tv_usec - last->tv_usec) / 1000L; if (reltime < interval) return 0; memcpy(last, sync, sizeof(*last)); return 1; } static int send_status_full(sock, netsendbuf, game, team) const int sock; struct netmessage_buffer *netsendbuf; const struct gameplay_public *game; const short team; { struct game_state_full buf; buf.team= team; memcpy(&buf.game, game, sizeof(buf.game)); return netmessage_send(sock, NETMSG_STAT, &buf, sizeof(buf), netsendbuf); } static int send_status_update(sock, netsendbuf, game) const int sock; struct netmessage_buffer *netsendbuf; const struct gameplay_public *game; { struct game_state_part buf; memcpy(&buf.stamp, &game->stamp, sizeof(buf.stamp)); memcpy(&buf.ball, &game->ball, sizeof(buf.ball)); memcpy(&buf.pad, &game->pad, sizeof(buf.pad)); return netmessage_send(sock, NETMSG_UPDT, &buf, sizeof(buf), netsendbuf); } static int peer_cnt_decrease(cnt) unsigned *cnt; { if (verbosity >2) printf("Peer count == %u (@%p).\n", *cnt, cnt); if (*cnt> 0) (*cnt)--; return (*cnt == 0); // time to remove entry } mmpong-0.9.1/server/linklist.c0000644000175000017500000000166711121720206015267 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include "linklist.h" void linklist_destroy(list, destroy_data) struct linklist *list; void (*destroy_data)(void *); { while (list) { struct linklist *elem= list; list= list->next; if (destroy_data) destroy_data(elem->data); free(elem); } } mmpong-0.9.1/server/linklist.h0000644000175000017500000000153411121720206015265 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __LINKLIST_HEADER__ #define __LINKLIST_HEADER__ struct linklist { void *data; struct linklist *next; }; void linklist_destroy(struct linklist *, void (*)(void *)); #endif mmpong-0.9.1/server/main.c0000644000175000017500000006427611132742361014377 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "addrtable.h" #include "thread.h" #include "lib/message.h" #include "lib/game.h" #include "config.h" #define BCAST_INTERVAL_MIN 10000 #define DEFAULT_BACKLOG 128 static struct config { // (-1) == unlimited in_addr_t addr; unsigned short port; char device[IF_NAMESIZE]; int n_threads; int max_connect; // concurrent connections int max_perip; // prevent flooding int max_payload; // kbytes/sec int client_timeout; // msec int min_rttm_interval; // minimum interval between synchronous two-way application-layer communication (`ACK`) (msec) short daemonize; short verbose; enum gamemode gmode; enum gamepadprofile gpads; } conf= { .addr= 0x00000000l, .port= 1212, .device= { '\x0' }, .n_threads= 1, .max_connect= (-1), .max_perip= 5, .max_payload= (-1), .min_rttm_interval= 8000, .client_timeout= 5000, .daemonize= 0, .verbose= 0, .gmode= gamemode_linear, .gpads= gamepadprofile_flat }; // communicate with worker threads short volatile signal_exiting= 0; unsigned bcast_interval= BCAST_INTERVAL_MIN; unsigned rttm_min= 0; struct gameplay_public game; pthread_rwlock_t gsync; int *insert_socket= NULL; in_addr_t *insert_peer= NULL; pthread_mutex_t *isync= NULL; unsigned * volatile n_connects= NULL; float * volatile sums= NULL; float * volatile variances= NULL; pthread_rwlock_t statsync; short n_teams; short verbosity; // not quite satisfied with this one yet // prototypes void sigterm_handler(int); static void parse_options(int, char **, struct config *); static int setup_listening_socket(const struct config *); static int setup_peer_socket(int, const struct config *); static int drop_privileges(void); static int collect_worker_threads(pthread_t *); static void free_globals(void); static void destroy_rwlocks(void); static void destroy_mutexes(void); static unsigned calc_bcast(int, unsigned, int); // returns appropriate interval measured in microseconds static int update_players(struct gameplay *, int, unsigned *, float *, float *); static int peer_cnt_increase(unsigned *); int main(argc, argv) int argc; char **argv; { parse_options(argc, argv, &conf); if (conf.daemonize) { pid_t pid= fork(); if (pid != 0) { // parent if (pid == (-1)) { perror("fork()"); exit (-1); } else { printf("Forked to background (pid == %d).\n", pid); exit(0); } } } if (!geteuid()) { // super-user -> set socket limit struct rlimit rlim= { .rlim_cur= RLIM_INFINITY, .rlim_max= RLIM_INFINITY }; if (conf.max_connect >0) { rlim.rlim_cur= conf.max_connect +5; // managing socket, stdin, stdout, stderr + one to spare rlim.rlim_max= rlim.rlim_cur +2; } if (setrlimit(RLIMIT_NOFILE, &rlim) == (-1)) { perror("setrlimit()"); if (errno != EPERM) exit(-1); } } int sock= setup_listening_socket(&conf); if (sock <0) { exit (sock); } int condition= drop_privileges(); if (condition <0) { close(sock); exit(condition); } if ((condition == 0) && (conf.verbose)) printf("Dropped user privileges.\n"); // set signal handler struct sigaction termact= { .sa_handler= sigterm_handler, .sa_flags= SA_RESETHAND }; sigemptyset(&termact.sa_mask); if ( (sigaction(SIGTERM, &termact, NULL)) || (termact.sa_flags= SA_RESTART, sigaction(SIGPIPE, &termact, NULL)) ) { fprintf(stderr, "Error initiating signal handler.\n"); close(sock); exit(-1); } // spawn if (conf.n_threads < 0) { // auto int ncpu=0; #ifdef __APPLE__ int mib[2] = { CTL_HW, HW_NCPU }; size_t len=sizeof(ncpu); sysctl(mib, 2, &ncpu, &len, NULL, 0); #else ncpu = sysconf(_SC_NPROCESSORS_CONF); #endif conf.n_threads = (ncpu > 0 ? ncpu : 1); if (conf.verbose) printf("Utilizing %d threads.\n", conf.n_threads); } assert(conf.n_threads >0); if (pthread_rwlock_init(&gsync, NULL) || pthread_rwlock_init(&statsync, NULL)) { close(sock); fprintf(stderr, "Error initializing rwlock objects.\n"); exit(-1); } if (atexit(destroy_rwlocks)) { close(sock); fprintf(stderr, "Error adding an exit() handler.\n"); if ( (pthread_rwlock_destroy(&gsync)) || (pthread_rwlock_destroy(&statsync)) ) fprintf(stderr, "Error destroying rwlock objects.\n"); exit(-1); } if (atexit(free_globals)) { close(sock); fprintf(stderr, "Error adding an exit() handler.\n"); exit(-2); } n_teams= 2; // fixed value for now verbosity= conf.verbose; if (conf.min_rttm_interval >0) rttm_min= (unsigned)conf.min_rttm_interval; insert_socket= calloc(conf.n_threads, sizeof(int)); if (conf.max_perip > 0) insert_peer= calloc(conf.n_threads, sizeof(in_addr_t)); isync= calloc(conf.n_threads, sizeof(pthread_mutex_t)); n_connects= calloc(conf.n_threads * n_teams, sizeof(unsigned)); sums= calloc(conf.n_threads * n_teams, sizeof(unsigned)); variances= calloc(conf.n_threads * n_teams, sizeof(unsigned)); pthread_t *threads= calloc(conf.n_threads +1, sizeof(pthread_t)); // NULL-terminated list assert(n_connects && insert_socket && ((conf.max_perip <= 0) || insert_peer) && isync && sums && variances && threads); pthread_mutexattr_t mutexattr; if (pthread_mutexattr_init(&mutexattr)) { free(threads); close(sock); fprintf(stderr, "Error initializing mutex attributes.\n"); exit(-1); } for (int tnum= 0; tnum< conf.n_threads; tnum++) { short half= 0; if ( (pthread_mutex_init(isync+tnum, &mutexattr) && // init mutex fprintf(stderr, "Error creating mutex objects.\n")) || (half++) || (pthread_create(threads+tnum, NULL, (void *(*)(void *))worker_thread, insert_socket + tnum) && // start thread fprintf(stderr, "Error spawning worker threads.\n")) ) { memset(threads+tnum, 0, sizeof(pthread_t)); collect_worker_threads(threads); free(threads); close(sock); if (pthread_mutexattr_destroy(&mutexattr)) fprintf(stderr, "Error destroying mutex attributes.\n"); for (int idx= 0; idx< tnum +half; idx++) if (pthread_mutex_destroy(isync+idx)) fprintf(stderr, "Error destroying mutex object.\n"); exit(-1); } } if (atexit(destroy_mutexes)) { collect_worker_threads(threads); free(threads); close(sock); fprintf(stderr, "Error adding an exit() handler.\n"); exit(-3); } if ( (conf.max_perip >0) && (!conf.verbose || printf("Initializing Flood Protection.\n")) && addrtable_init(conf.max_connect, &mutexattr)) fprintf(stderr, "Error allocating hash map, dropping flood protection.\n"); if (pthread_mutexattr_destroy(&mutexattr)) { collect_worker_threads(threads); free(threads); close(sock); if (addrtable_is_active()) addrtable_destroy(1); fprintf(stderr, "Error destroying mutex attributes.\n"); exit(-2); } // listen if (listen(sock, (conf.max_connect >0)? (conf.max_connect+19)/20 : DEFAULT_BACKLOG) == (-1)) { collect_worker_threads(threads); free(threads); close(sock); if (addrtable_is_active()) addrtable_destroy(1); perror("listen()"); exit(-1); } // begin game struct timeval cur_time; gettimeofday(&cur_time, NULL); srand(cur_time.tv_sec); struct gameplay igamelocal; if (gameplay_init(&igamelocal, sizeof(struct gameplay), conf.gmode, conf.gpads) != PONG_SUCCESS) { fprintf(stderr, "Compile server against new lib version headers.\n"); collect_worker_threads(threads); free(threads); close(sock); if (addrtable_is_active()) addrtable_destroy(1); exit(-1); } netmessage_scrambler_init(); // accept loop condition= 0; for (;!signal_exiting;) { fd_set rfds; FD_ZERO(&rfds); FD_SET(sock, &rfds); struct timeval tv_select= { .tv_sec= 0, .tv_usec= 200 }; // remain responsive to signals // note: don't rely on the value of tv_select after the call int retselect = select(sock+1, &rfds, NULL, NULL, &tv_select); // update game progress and broadcast interval unsigned cur_connects= 0, emptyteam= 0; for (int teamidx= 0; teamidx< n_teams; teamidx++) { unsigned teamcount= 0; for (int threadidx= 0; threadidx< conf.n_threads; threadidx++) teamcount+= n_connects[threadidx * n_teams + teamidx]; if (teamcount) cur_connects+= teamcount; else emptyteam++; } bcast_interval= calc_bcast(conf.n_threads, cur_connects, conf.max_payload); if (emptyteam) { // wait for players if (game.status != gamestatus_onhold) { if (pthread_rwlock_wrlock(&gsync)) fprintf(stderr, "Cannot acquire write lock.\n"); game.status= gamestatus_onhold; if (pthread_rwlock_unlock(&gsync)) fprintf(stderr, "Cannot release write lock.\n"); } } if (cur_connects) { // semantics on this lock may seem strange, but make perfect sense, since write operations are non-overlapping if (pthread_rwlock_wrlock(&statsync)) fprintf(stderr, "Cannot acquire write lock.\n"); update_players(&igamelocal, conf.n_threads, n_connects, sums, variances); if (pthread_rwlock_unlock(&statsync)) fprintf(stderr, "Cannot release write lock.\n"); } if (!emptyteam) { gettimeofday(&cur_time, NULL); if ((condition= gameplay_update(&igamelocal, &cur_time)) != PONG_SUCCESS) fprintf(stderr, "Failed to update the game state (%d).\n", condition); condition= 0; } if ((cur_connects)||(igamelocal.status != game.status)) { // publish updated state struct gameplay_public gamelocal; gameplay_internal_to_public(&igamelocal, &gamelocal); if (pthread_rwlock_wrlock(&gsync)) fprintf(stderr, "Cannot acquire write lock.\n"); memcpy(&game, &gamelocal, sizeof(struct gameplay_public)); if (pthread_rwlock_unlock(&gsync)) fprintf(stderr, "Cannot release write lock.\n"); } if (!retselect) continue; // timeout condition if (retselect == (-1)) { perror("select()"); if (errno == EINTR) continue; // signal caught condition= 1; goto leave; } // FD_ISSET(sock, &rfds) will be true struct sockaddr_in peer; unsigned peerlen= sizeof(peer); if (verbosity) printf("Client knocking.\n"); int intro= accept(sock, (struct sockaddr *) &peer, &peerlen); assert(peerlen == sizeof(peer)); if (intro == (-1)) { if (errno == EINTR) continue; perror("accept()"); if ((errno == EMFILE) || (errno == ENFILE)) continue; // too many open files condition= 1; goto leave; } if (verbosity >1) printf("Considering client [%d]\n", intro); // flood checks int flood= ((conf.max_connect >0) && (cur_connects >= conf.max_connect)); if ((flood == 0) && addrtable_is_active() && (getpeername(intro, (struct sockaddr *) &peer, &peerlen) == (-1))) { perror("getpeername()"); fprintf(stderr, "Shutting down flood protection.\n"); addrtable_destroy(0); } if ((flood == 0) && addrtable_is_active()) { int atomic= addrtable_atomic(peer, peer_cnt_increase, NULL); if (atomic< 0) { fprintf(stderr, "Error with address map, disabling flood protection.\n"); addrtable_destroy(0); } if (atomic> 0) flood= 1; } if (flood) { char *msg= "No more connections"; netmessage_send(intro, NETMSG_KICK, msg, strlen(msg) +1, NULL); close(intro); continue; } if (verbosity) { char namebuf[INET_ADDRSTRLEN]; const char *name= inet_ntop(AF_INET, &peer.sin_addr, &namebuf[0], INET_ADDRSTRLEN); printf("Accepted client%s%s\n", (name)?" ":".", (name)?name:""); } if (setup_peer_socket(intro, &conf)) { close(intro); continue; } // assign a worker thread to the client int load= 0; // determine the best-suited one (measured by client load) unsigned lcon= 0; for (int idx= 0; idx< conf.n_threads; idx++) { if (idx * n_teams > cur_connects) break; // resolves team assignment issues for very small numbers of peers unsigned ccon= 0; for (int tm= 0; tm< n_teams; tm++) ccon+= n_connects[idx * n_teams + tm]; if (!idx) lcon= ccon; if (ccon < lcon) { load= idx; lcon= ccon; } } for (int greed= 0; greed< 3; greed++) { for (int idx= 0; idx< conf.n_threads; idx++) // round-robin if ((insert_socket[(idx + load) % conf.n_threads] == 0)||(greed == 2)) { if (greed) { if (greed == 2) while (insert_socket[ (idx + load) % conf.n_threads ] != 0) usleep(5000); if (pthread_mutex_lock( isync+ ((idx + load) % conf.n_threads) )) { fprintf(stderr, "Error acquiring mutex lock.\n"); condition= 1; goto leave; } } else if (pthread_mutex_trylock( isync+ ((idx + load) % conf.n_threads) )) continue; // minimize blocking occurrences if (verbosity >1) printf("Inserting client [%d] into thread %d:%d\n", intro, (idx + load) % conf.n_threads, insert_socket[ (idx + load) % conf.n_threads ]); insert_socket[ (idx + load) % conf.n_threads ]= intro; if (insert_peer) insert_peer[ (idx + load) % conf.n_threads ]= peer.sin_addr.s_addr; if (pthread_mutex_unlock( isync+((idx + load) % conf.n_threads) )) fprintf(stderr, "Error releasing mutex lock.\n"); intro= (-1); break; } if (intro == (-1)) break; } if (intro != (-1)) { fprintf(stderr, "Fault: No worker threads available.\n"); condition= 1; goto leave; } } if (verbosity) printf("Exiting on behalf of SIGTERM signal.\n"); leave: collect_worker_threads(threads); free(threads); close(sock); addrtable_destroy(1); return condition; } void sigterm_handler(signal) int signal; { if ((signal == SIGPIPE)&&(conf.verbose)) printf("Caught SIGPIPE.\n"); if (signal == SIGTERM) signal_exiting= 1; } static void parse_options(argc, argv, conf) int argc; char **argv; struct config *conf; { const char *short_options= "a:bc:d:hi:l:m:n:p:r:t:Vv"; const struct option long_options[]= { { "model", required_argument, NULL, 'm' }, { "paddles", required_argument, NULL, 'r' }, { "address", required_argument, NULL, 'a' }, { "port", required_argument, NULL, 'p' }, { "dev", required_argument, NULL, 'd' }, { "background", no_argument, NULL, 'b' }, { "nthreads", required_argument, NULL, 'n' }, { "max-connect", required_argument, NULL, 'c' }, { "max-per-ip", required_argument, NULL, 'i' }, { "max-payload", required_argument, NULL, 'l' }, { "min-rttm-interval", required_argument, NULL, 's' }, { "client-timeout", required_argument, NULL, 't' }, { "verbose", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { NULL, 0, NULL, '\0' } }; const char *options_desc[]= { "Game model to play (defaults to \"linear\")", "Paddle profile to be used by all teams (defaults to \"flat\")", "Local hostname or IP address to bind to", "Local port to listen on", "Network device to bind the socket to", "Daemonize and return", "Number of threads to run simultaneously (-1 == auto)", "Maximum number of clients to serve simultaneously (-1 == unlimited)", "Maximum number of clients to allow to connect from one host (-1 == unlimited)", "Restrict traffic to specified amount of kBytes/sec (-1 == unlimited)", "Minimal interval between near-synchronous packet transfers (-1 == unrestricted)", "Timeout between status updates before client session is terminated, measured in msecs (-1 == no timeout)", "Log more messages to the console", "Show this help screen", "Show version information", NULL }; const char *options_int= "pncilt"; extern char *optarg; extern int optind; struct { char key; int *val; } map[]= { { 'n', &conf->n_threads }, { 'c', &conf->max_connect }, { 'i', &conf->max_perip }, { 'l', &conf->max_payload }, { 't', &conf->client_timeout }, { 's', &conf->min_rttm_interval }, { '\x0', NULL } }; int opt, idx, showhelp= 0, doexit= (-1); while (( opt= getopt_long(argc, argv, short_options, long_options, NULL) )) { if (opt == (-1)) break; switch(opt) { case 'm': if (gameplay_parse(optarg, &conf->gmode, NULL) != PONG_SUCCESS) { fprintf(stderr, "Model not implemented (%s).\n", optarg); doexit= 1; } break; case 'r': if (gameplay_parse(optarg, NULL, &conf->gpads) != PONG_SUCCESS) { fprintf(stderr, "Paddle profile not implemented (%s).\n", optarg); doexit= 1; } break; case 'a': conf->addr= inet_addr(optarg); if (conf->addr == INADDR_NONE) { struct hostent *hostptr= gethostbyname(optarg); if ((!hostptr)||((hostptr->h_addrtype & AF_INET) == 0)||(!hostptr->h_length)) { fprintf(stderr, "Invalid IP Address (%s).\n", optarg); doexit= 2; } else { assert(sizeof(conf->addr) == sizeof(hostptr->h_addr_list[0])); memcpy(&conf->addr, hostptr->h_addr_list[0], sizeof(conf->addr)); } if (conf->addr == INADDR_NONE) { fprintf(stderr, "Cannot determine IP Address of host %s.\n", hostptr->h_addr_list[0]); doexit= 3; } } break; case 'p': conf->port= atoi(optarg); break; case 'd': strncpy(conf->device, optarg, IF_NAMESIZE); conf->device[IF_NAMESIZE -1]= '\x0'; break; case 'v': printf("mmpongd %d.%d (r%s.c%d.l%d)\nReleased under the terms of the GPL (http://www.gnu.org/licenses/gpl.html).\n", VER_MAJ, VER_MIN, VER_SVN, (int)sizeof(struct gameplay), gameplay_getversion()); doexit= 0; break; case 'b': conf->daemonize= 1; break; case 'h': showhelp= 1; break; case 'V': conf->verbose++; break; default: for (idx= 0; map[idx].val; idx++) if (map[idx].key == opt) { *map[idx].val= atoi(optarg); break; } if (!map[idx].val) { fprintf(stderr, "Parameter not recognized (%d).\n", opt); exit(-1); } } } if ((optind < argc) || (showhelp)) { printf("Usage: %s", argv[0]); for (idx= 0; long_options[idx].name; idx++) { printf(" [--%s", long_options[idx].name); if (long_options[idx].has_arg == required_argument) printf("=%s", (strchr(options_int, long_options[idx].val))? "%d":"%s"); printf("]"); } printf("\n"); for (idx= 0; options_desc[idx] && long_options[idx].name; idx++) { printf("\t-%c%s, --%s%s :\t%s\n", long_options[idx].val, (long_options[idx].has_arg != required_argument)? "": ((strchr(options_int, long_options[idx].val))?" ":" "), long_options[idx].name, (long_options[idx].has_arg != required_argument)? "": ((strchr(options_int, long_options[idx].val))?"=":" "), options_desc[idx]); } doexit= (optind < argc)*99; } struct { char *name; int *limit; } limits[]= { { "Number of threads", &conf->n_threads }, { "Connection limit", &conf->max_connect }, { "Connections per IP", &conf->max_perip }, { "Payload limit", &conf->max_payload }, { "Client timeout", &conf->client_timeout }, { "RTTM Interval", &conf->min_rttm_interval }, { NULL, NULL } }; for (idx= 0; limits[idx].limit; idx++) if ((*limits[idx].limit< (-1)) || (*limits[idx].limit == 0)) { fprintf(stderr, "%s requested is invalid (%d). Use numbers >0, or (-1) for unlimited.\n", limits[idx].name, *limits[idx].limit); doexit= idx+1; } if (doexit != (-1)) exit(doexit); } static int setup_listening_socket(conf) const struct config *conf; { int sock; if ((sock= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == (-1)) { // let's start out with TCP perror("socket()"); return (-1); } #ifndef __APPLE__ if ((strlen(conf->device)) && (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, conf->device, strlen(conf->device) +1) == (-1))) { close(sock); perror("setsockopt()"); return (-2); } #endif int retcode= -2, keepalive= 1, reuse= 1, rcvlow= 2; int maxseg= sizeof(struct netmessage); struct timeval timeout; memset(&timeout, 0, sizeof(timeout)); timeout.tv_sec= conf->client_timeout / 1000; timeout.tv_usec= ((suseconds_t)(conf->client_timeout % 1000L)) * 1000L; if ( (--retcode, setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == (-1)) || // we expect to receive short integers (--retcode, setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, &rcvlow, sizeof(rcvlow)) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == (-1)) || // ideally, we don't send or receive more data than a netmessage contains (--retcode, setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &maxseg, sizeof(maxseg)) == (-1)) ) { close(sock); perror("setsockopt()"); return retcode; } // bind to port struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family= AF_INET; addr.sin_addr.s_addr= conf->addr; addr.sin_port= htons(conf->port); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) == (-1)) { close(sock); perror("bind()"); return (-9); } return sock; } // set up socket for close-to-realtime data transfers static int setup_peer_socket(sock, conf) int sock; const struct config *conf; { int retcode= 0, yes= 1, rcvlow= 2; int maxseg= sizeof(struct netmessage); #ifdef __APPLE__ int tos= IPTOS_LOWDELAY; #else uint8_t tos= IPTOS_LOWDELAY; #endif struct timeval timeout; memset(&timeout, 0, sizeof(timeout)); timeout.tv_sec= conf->client_timeout / 1000; timeout.tv_usec= ((suseconds_t)(conf->client_timeout % 1000L)) * 1000L; if ( (--retcode, fcntl(sock, F_SETFL, O_NONBLOCK) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)) == (-1)) || // we expect to receive short integers (--retcode, setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT, &rcvlow, sizeof(rcvlow)) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == (-1)) || (--retcode, setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == (-1)) || (--retcode, setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == (-1)) || // ideally, we don't send or receive more data than a netmessage contains (--retcode, setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &maxseg, sizeof(maxseg)) == (-1)) || (--retcode, setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == (-1)) // (--retcode, setsockopt(sock, IPPROTO_TCP, TCP_CORK, &yes, sizeof(yes)) == (-1)) // this last one is a possible portability issue (remove if required) ) { perror((retcode == (-1))?"fcntl()":"setsockopt()"); return retcode; } return 0; } static int drop_privileges(void) { if (geteuid()) return 1; // not super-user const struct passwd *pwd= getpwnam("nobody"); if (!pwd) { perror("getpwnam()"); return (-1); } #if defined __USE_BSD || (defined __USE_XOPEN && !defined __USE_UNIX98) const char *shell; while (( shell= getusershell() )) if (!strcmp(pwd->pw_shell, shell) && (strcmp(pwd->pw_shell, "/sbin/nologin"))) break; endusershell(); if (shell) { fprintf(stderr, "User nobody is not safe\n"); return (-2); } #endif if (setgid(pwd->pw_gid)) { perror("setgid()"); return (-3); } if (setuid(pwd->pw_uid)) { perror("setuid()"); return (-4); } return 0; } static int peer_cnt_increase(cnt) unsigned *cnt; { if (conf.verbose >2) printf("Peer count == %u (@%p).\n", *cnt, cnt); if (*cnt >= conf.max_perip) return 1; // limit reached (*cnt)++; return 0; // success } static int collect_worker_threads(threads) pthread_t *threads; { signal_exiting= 1; // signal to exit int threadval, retval= 0; for (int idx= 0; threads[idx]; idx++) if (pthread_join(threads[idx], (void *)&threadval)) { fprintf(stderr, "Error joining worker thread.\n"); retval= (-1); } return retval; } // atexit() handler static void free_globals(void) { if (n_connects) free(n_connects); if (insert_socket) free(insert_socket); if (insert_peer) free(insert_peer); if (isync) free(isync); if (sums) free(sums); if (variances) free(variances); n_connects = NULL; sums= variances= NULL; insert_socket= NULL; insert_peer= NULL; isync= NULL; } // atexit() handler static void destroy_mutexes(void) { for (int idx= 0; idx< conf.n_threads; idx++) if (pthread_mutex_destroy(isync + idx)) fprintf(stderr, "Error destroying mutex object.\n"); } // atexit() handler static void destroy_rwlocks(void) { pthread_rwlock_t *locks[]= { &gsync, &statsync, NULL }; for (int idx= 0; locks[idx]; idx++) if (pthread_rwlock_destroy(locks[idx])) fprintf(stderr, "Error destroying rwlock object.\n"); } // return value is of dimension microseconds static unsigned calc_bcast(n_threads, cur_connects, max_payload) int n_threads, max_payload; // measured in kBytes/sec unsigned cur_connects; { if ((max_payload <= 0) || (n_threads <= 0)) return BCAST_INTERVAL_MIN; unsigned interval= (unsigned) (((unsigned long)cur_connects) * n_threads * (NETMESSAGE_MAXLEN /2) *1000 *1000 /max_payload /1024); if (interval < BCAST_INTERVAL_MIN) interval= BCAST_INTERVAL_MIN; return interval; } static int update_players(igame, n_threads, n_connects, sums, variances) struct gameplay *igame; int n_threads; unsigned *n_connects; float *sums, *variances; { float mean, var; unsigned tcons; for (int team= 0; team< n_teams; team++) { mean= var= 0; tcons= 0; for (int idx= 0; idx< n_threads; idx++) { tcons+= n_connects[idx * n_teams + team]; mean+= sums[idx * n_teams + team]; var+= variances[idx * n_teams + team]; } if (tcons >0) { mean/= tcons; var/= tcons; } if ((mean < 0.f)||(mean > 1.f)||(var < 0.f)) return (-1); // I'm not sure baby-sitting our user-base is the right thing to do, but here we go: if (mean > 1.f - igame->pad[team].size / 2) mean = 1.f - igame->pad[team].size / 2; if (mean < igame->pad[team].size / 2) mean = igame->pad[team].size / 2; igame->pad[team].mean= mean; igame->pad[team].var= var; igame->pad_attr[team].peers= tcons; } return 0; } mmpong-0.9.1/common/0000755000175000017500000000000011354734770013265 5ustar andreandremmpong-0.9.1/resources/0000755000175000017500000000000011354734770014007 5ustar andreandremmpong-0.9.1/resources/CEGUI/0000755000175000017500000000000011354734770014643 5ustar andreandremmpong-0.9.1/resources/CEGUI/fonts/0000755000175000017500000000000011354734770015774 5ustar andreandremmpong-0.9.1/resources/CEGUI/fonts/Font.xsd0000644000175000017500000000321011130511363017375 0ustar andreandre mmpong-0.9.1/resources/CEGUI/fonts/FreeSans-10.font0000644000175000017500000000024211354706475020607 0ustar andreandre mmpong-0.9.1/resources/CEGUI/fonts/FreeSansBold.ttf0000644000175000017500000051764011354706475021040 0ustar andreandre FFTMMtGDEFf foGPOS^Ky&tGSUBVqhOS/2]>VcmapK9%Fcvt  }-| fpgmB,gaspo glyf"R>?|Τhead,6hhea d$hmtxe#localW~-maxp  nameP$|n post1H Zprep8F',%`z9&_< VV840Z8$ o /]1 @ GNU  8,`l!MMp2,,y72M(MH2@M@,,D,,,,, ,,,MqMqH(H2H(c@R,MOcJ *D?,JcPABD (L +P cL cMBMH=,M,c;,"c,Mc"cCC,;Cy<c?c#c:c?,Mc:, ,, %dHH<MB,$,,,d,!Mr,XH(MM^H8__Myc:,@M,(m,Xe(e(ec4,OOOO?D ( ( ( ( (HO LLLLLcC,,,,,,y,",,,,Cc#c?c#c#c#c#c#H2c c:c:c:c:, c:, ,,,,,",,",,",,"McO,O,O,O,O, *c" *c" *c" *c"DcCc""?C(?C,J,;/<cPCcPCcPjCcP,CcDc?Dc?Dc?c?Dc? (c# (c# (c#P?P?P7 , , , ,cMccMLc:Lc:Lc:Lc:Lc:Lc:  , cccMc,Rc;c,,2",Rc;aO ( cM *yC) J,;Ml: c? ((#W( ,c:< ,$M,McL:<lc,,#9Cc@`7drdH-p5MMWPyP,CDDy?, (c#Lc:Lc:Lc:Lc:Lc:,,,y *c" *c"J,; (c# (c#,,5MMW *c"vBDc?,y c ,,O,O, (c# (c#PP?Lc-Lc: ,cM9DcC!,O, (c# (c# (c# (c#, ,c;,"X cc,,B- 4:c"c",",  k:cCcCXC*C Cy<y<y<cc?g<^$n$6 H;;;????l<lA,MM HMo"B, , ,;,Bc@c@c@c#*<:h"[<(,;<cc@c@ yy&"+)l 44CiiMtMcM M M M MMMM MMMyMdMMNM@MMMMM^MWMMMMM#MpMMM-MMMZt :kuN99M MMIMMM M MLM@/Mq MMM)&.>;3h7#5H<0YCC5)>5]m.[j{8{:MMqk]MMq`c;IME=  0d8x0Oa`2;[FJcSN-8x ;8W8 D Yl ,,6  (HRR cR$ :R,@ L.ZR&RRRRRcR#R4  RRR$jRR$RR$c$0R<!RRERR"ER($Q**<<=$- g<g</<8<[<^$[<d<,$% $ <*$v<< <8<, P<J=$=$^<,$+ <<r/<g<% \<@F<$^$z;>@R<cRd<|R<nR><H #*+ R=<R*< + |9R<R<QQ<,.$,.$f  = [$ Dg0<L<FLF*   :R4 )R<$zR[<3R<DL<R<:R4$6&$R=$,=",="4  - ,(Rg<Rg< (c#$^$$^$",     <*$cR<R < "$A +d$ R*<FF-AF!<FFWF-FFFI*A#<#Fv#-A-2AAA-$F(F2#%##McMM` ji<eAz|AQ<#FeAA#aF>FDF#eA_Fc#rAkjADeAOAi7A#[AXArAAeAd(\FAhA^##rAMq+e~+?)OeMq=M-/~?O)O?OLIN>SL>PC)OIL<HOh=PX?HL>*O&II22z9(.8302-!v| ;   vVh<<<G<s<v<;<P<v<<^<`PCPv<b$<gP7<f<s<H<s<2bPPV<P<`Pvu<4o~ 4?,Rc;Rc;Rc;,,"McMcMcMcMcO,O,O,O,O,cJM *c"DcCDcCDcC$cCDcCJ,;J,;J,;cPCcPcPcPABy<ABy<ABy<Dc?Dc?Dc?Dc? (c# (c# (c# (c#Lc:Lc:P?P?P?P , , , , ,cMcMcMcMLc:Lc:Lc:Lc:Lc:,,          ,,, ccccCM , +M,,,,,,,,,,,,O,O,O,O,,O,O,O,?C?C (c# (c# (c# c (c# (c# (c#(#(#(#(#(#Lc:Lc:L:L:L:L:L:, , , , xxxxxxxxaQ'````````7?;;uwzvrzTC<@bbbbbb g<7888888888[        E\&0xx``;;bb88  xxxxxxxx% ````````HQ        ~RvxxxxxxTZ:TOM``0``OVPTTObopanQMQM8888SS88&MMMR     )9MuQM,dMM,,d,CBBBGIHI,,^2^-@\\@ W 44f4{;MSMPn=pf,""H5SN`N@@p,Mq"zA8" %W4%%%%\9%%__}_____ __________-(_______ _^^^^_^AcO y<.PPD$]O,Z,+*(_=O-9EH NJ ^q;.,G ;,J,L"=M-/~?g(& Cc c 0 "e(ee(eeee(ee(eeee(?,?B???cP,MABC,CBCBC,BXnBC,BXC,"cy<'@*@'@'''B'B'''' '''', ;X)H(eR`d$2H-H-  rSrSM&,7 7 7,`,,VM(m G-&!"&s h-Wh-G2$ y G G h - Gm_@[QQAAAAAI1IV=x?OML>PR?>H2HLHLHLHL===M-/~?O\Lb)N>SL>CILHO=PX?HL>OM&L8OS#< ~37ouz~_sV_   ( 0 3 6 9 < B H M \ ^ tEMWY[]} d q !!!! !!!!!"!'!+!.!2!8!;!D!K!N!!!!"""""""+"`"e$#%&,&g&o'@.6<>ADO $7Ptz~p1Ya     * 2 5 8 < > G K Y ^ f HPY[]_ p t !!!! !!!!! !&!)!.!2!5!:!A!K!M!S!!!"""""""+"`"d$#%&,&`&i'@.8>@CF~fdZJ<&  whed`\VTRPNMLKJHGFDCA@?42RQOFD@0<51[    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a_rdeiaxpkvj\s]^gwSW@[l|)cnZTUm}ebRS[\WX`:xy`yY]oYoqklmzpnZ,K*PXJvY#?+X=YK*PX}Y ԰.-, /+\X G#Faj X db8!!Y!Y-,KRXE#Y!-,i @PX!@Y-,+X!#!zXYKRXXY!+XFvYXYYY-, \Z-,"PX \\Y-,$PX@\\Y-, 9/-KPXYF+X!YKRX!Y+\X E+D E++D E/++D E ++D Ev++D E?++D E ++D E+D E +Fv+D E 5+Fv+DYvjr!y****BZ^nFXNj,@R2Zz :Jd|.l~(6H\jx  2 d  , : j  R r  D R > | "f,:`|2>bt&r|0<HT`lx"^jv&2> ,8v&2>Jt.:FR^jv&N "Hp|(4@LXdlx ".:Fd2zJ(4@Rr~P\ht  b j ! !T!\!!!!""6"n"""# #T#r####$*$Z$~$$%%<%v%%&&N&&&' '6'Z''''((0(\(((()")N)))***"*@*H*T*`*l*x***********+++$+0+@+L+\+h+x++++++++,,X,d,p,|,,,- --"-.-:-F-R-^-j-v-------.. ..$.0.<.H.T.`.l.x..........// //&/2/>/J/V/b/0000H0r0~00111*1:1F1R1^1n1z1111112&202t22333h3p3x34 4.4r445 5b555566.6X666677B7l7777828d8n88889 9&9P9Z999::N:X:x::;;;(;2;F;l;;<<0<\> >>>(>2><>F>P>X>d>x>>>>>? ??&?8?B?T?f?r?z????????????@@@6@B@d@~@@@@@@AAAA,A@ATAhAxAAAAAAAAAAABBBB"B0B@BJBTBfBvBBBBBBBBBBCCC(C2CLJLVLLLM$M^MMMNN NVNNNNOO8OrOOOP PRPzPPPPPPQ8QlQxQQQRR@RRRRRRSS0S8S@SLSTSSSSSSTTTBTJTZT~TTTTUU"U@UHUPUXUlUtU|UUUUVV$VaXataaaabbTbbccc2cHc^c|ccccd d,dJdlddde ePefff"f.fXf|ffffgg4gTgrgggggggh h`hlhxhhhhhhhhii ii"i.i:iBiJiRiZifiri~iiiiiiiiiijjj*jbjjjjkkkk4kVkkkklldlxllmm4mDmnmmmnJnnnnoo$oXooop2pXpppqqjq|qqr(rnrvrrrrrss>s`sssstt0tpttttu2uPupuuvv6vRvxvvw w:w|wwwxx4xxxxxyy\y|yyyyyyyyzz zz(z8zFzRz`zjzxzzzzzz{{"{J{V{h{{{{| |.|R|r||||}}H}d}}}}~~*~>~T~d~|~~ nĀvBr*XL|>„(R.x҆<Ĉ*XnȈ܈P~ފ\2^:~ Bpʍ$dPFvLjʾ HT`l̾־ Jh 8FZrTn &0:DNXblv€Š”ž¨²¼N^n~̞̮̺̎".6BRZbjrz͖͆ͪ͢Ͷ "*@NXf΄ΒΠήμ &4>LjtϐϞϨ϶(2j،ش @lٔ٪,Nڊںxۖܢ".:FR^jv݂ݎݚݦݲݾ*2>Jް߄ߔߤߴFxHr*`H0^p,dv&2>JVbnzR!*./<2<2/<2<23!'3#! f!Xp #5#5*C)52 #'5!#'5*8(v*8(ق)#7#537#53733733#3##7#737#j(Macw"i"g"i"Zmdw(i(ggg ccccc!&+.=35&546753#54&'#5654jsUkaD_n)%tdqgDQT v[ j/Th 66p^,7 bR`m i)L?LI_ $-2#"&46"2654%3#2#"&546"2654MkmKLlloF22F2;LuMMkmKLlloF22F2kMIkkkc1"#11"#'tkNIkkLKkc1F11"#7!*53#'#"&5467&/&546265327654&#"q${.4&'3zdO=7>54&#"#&546323NRL,EZu@5/05{np;`A7A"}bs?RO;5=B; p}z+4>32#"&'332654&'퀆&#"#&/@9gz,1<8qmd.;*#6 |.*7(7T0 iX1GU>h{}jo?  ##5!53# JtvL/!632#"&'332654&#"#9Bb{rg]5==5C~?}+mss]XH>@I6 )!2#.#">32#"&'&$"32654 $=/+'"*9 !@,[sj?i3(b<<0/=Q"F0"60K"ft81QI;8HJ9; #67>7!5~v Cg n΍.#}  +#"&5467.54632"3264"2654&1+=7op7=0,cb^;;/.;k2=>37A)#"&'3325#"&54632'"2654&"3HB&\{/%u3^vj">E3!/78`<;r`T3kR&no/JIzGH:>Jq%#5#5vqR 7356=##5q ?1VV#28$8b( %5~moy24!5!5wwww(5-5(~ moy@,#>32#54>54&#"#5@!1D;!n2F-|#22#<-/?Ab7" f?R1 &12K.(4!1BL: v9E%4>327332654'&#"327#"&5467632#"&'#"&2654&#"5lFO#ZN5lbhp_Օt[eMBϵ0KW(1A >XD`<[-';a4WgHH> \vX\qj)I.WDݩCtJ*+ KdC/7L-> %!#3# 1Y__'R%#!!23254#3254#5jIEDc1.6yNjjoo$DB((?</D#G RS޽^_,&#"32673#"&54632YdaWFT ⃈ywLF}ͰΊyM 3!2#'3254+MTm%YYG(,jhU}Op !!!!!]:}}}JJ #!!!?:}}* &#"32>7#5!#'#"&54632:(_mu[%4,"$Z*hJĝuvp"='}v`=8ئԄqD !#3!3#ߖ K'?#Ֆ'".=332653)KF*&+'*z/[?HF2-,)foJ 7#33 #B@aBPC!!] }B #33##ؖ▁8D'8D !#33ۖ!'( 6632#""32654&(¿[nn[ZomPծ|拊pvLy #!2#'3254&+Bryxdu9<tno}m86+2'#"&6654&#"327'+%PLVSjR!m\[nnZ4$MؤJ-LQQ2N1NB\tIP(%4654&+#!2#.4&+326,18Q){   4655}" .)&>?"0 @>=*08%& y)#&#"#"&'332654&/.54632_:D:Pre\TLDO>Kfo^&GP:'r1)('_Vo}|u;>4.,0ZUku 1WV##5!H\\}}L".53 53PKO:%%:O$8];;]8$!#33%  !# #333ڇzwΟqqvm9"# #'#373게޲toj#33򧕋BB !!5!5Btu}!}}}B84#3#4ppf+f!#7C(853#53ppff_= # #3 qysp},CB!5BxEE_3#~FF %*"&54?>54#"#632#&575326JZ8(!Q4* JI(0,$)#5:YI  6 *%"M&  "#B;?2#"'#36"32654\aac22xhBA54B%}zN7Ou]JM^^JK" %"32673#"&54632#. r=5). bxye} -OZ-4aqpc5-!"&546323#5"2654&dab34B4BBhBAyzO'7N_KJ^\JM_ %2!3273#"&546.#"#BE3  -"P[s71\%*AkE -+DP_;@{9###535432&#"9SLL)'&]L]Ai*5"&%&3#"&'3326=#"&54632"32654&zlR5B'C/dy`Z27EE_iSF?=1I,"~}\`MI\\IKbC4#"#3632#V/=:d 97 Jd<.W'N5C#7#5ό}}& #"'532657#5:A$/ B:p[}};$ 73#'#3DZŤ8J=C#ό'<8% 3>32632#4&#"#4&#"#<#C.j)NWbYMa:0#9% 2#"&6"32654&-{}}9FF98GE% q``_LP_:&>% 2#"'#36"32654\f|ad22yjAA54B%XPYu^]^IM& % "&463253#"32654d}ad21jAA54BYP X^]\JO?r%3>32&#"#?P0 {j7<{%(732654&/.54632#&#"#"'+<17 ''wgm|d%,j1bC"$46%SaaVI 3i;:#-#327#"5#5353-N "/EE]$b L]:!#5#"&5332653:hV_Z3>@WbYxa:0R!#33^umu !# #333eQVVRQR}zz #'#373c[\XW  %533#"'5326ɚwp!B+%22w !i4 !!5#5A qqq:q%8="74&#'536=46;#";#"&5v.AE6L%-)88)%L6E,\>=Oc97. .7%cO=d8#P_H8`"3#+532=467.=4&+532.AE6L%-)88)%L6E{,\>=Oc97. .7%cO=<:%2673#"/&#"#632!N v-&9  Nx0#9"#*#*BF H$ z>73#5.546753#./"( zY,hwrm,\y (OS-.Ztef} VUu]..S93#632327#"&#"'>54'#53.54632#'.#" z4@N"P/:)LE##1<7?/Y<"l.,??-,?(#3##5#535#53373񎎎ˋ_;5;;5;ad8##PPP{{!74@.54632#&#"#"'332654./&546'654&} v[%>=& D#%5a,2Fxb$+"*  O' *.*Ka$K5M#F6c2A1j ')"d.V-=QL(P !m:#5!#5n(nzzzz!-#&#"3273#"&54632$ #"&54%"32654&(ZM5=;4X\fT\mn^Ne<ᢛ~}{NPNVOZ~jk}T❞I+!5%#&'#"&54?>54#"#6 '5326A$\ +7,6_"1N R .#TNNF .6,Wpdf'(XH 757757Xee.eeruYYrruYYr(V w!#5!(MUw*V#.:#32#&546=4#'3264&+& #"&54%"32654& `9@Fg=\g$$gM<ᢛ~}{9@9J" %), 61Q>❞;!5;OO 2#"&46"32654&/>YY@=YY?%44#&44Y>@XZ|Y?4%#54%$48`%!5##5#5353)װwwgwwIwwH3!>54&"#&54632H(4J!08/ :\QKINH*>"6Q.H'&,"%" EJEI"632#"&'332654'#53254&#"BO4BWHIQ\=#T K!;@59 G?KKDB"=>:&$y_=#7=~FF:$=732653327#"&'#"'#3T,9  !%#*4#='n<.Rz,W&A463!####.wX/+b?b\tcHP@$3#@||${$&;632#"'732654&#"+#)C>7SE$!?&!-04 (#5273#ku@_==KWW!52"&546"2654NӖLTVVVlD**D*TNN\RN[\OP\D9\:9-/XH 57'557'5eeveeOsuYYrsuYYr(R #5273#3#%##5#53#5ku@_WYW._m_j==KW!K]]J(N *#5273#3#3!>54&"#&54632ku@_WYW(4J!08/ :\QKIN==KW!@*>"6Q.H'&,"%" EJER"&14632#"&'332654'#53254&#"%3#%##5#53#5BO4BWHIQ\=#T K!WYW._m_j;@59 G?KKDB"=>:&$!K]]J44  "`#C$#v$#Y$#o$#j$#m$%##!!!!!!#5SCr}}}G,$1&#"32673632#"'732654&#"7.'&54632YdaWFT#)C>7SE$!+EK$o ⃈ywLF|(&!-04 S !"jıΊyOp#C(Op#v(Op#Y(Op#j(#C,?"#v,+#Y,#j,#53!2#!4&+3#326MMMWl$*./)$mVZUR]SP6'-34-(loPjD#o1(#C2(#v2(#Y2(#o2(#j2O ''7'7TTTTTTTU##"''7&54632732654&#"WS3VRŸ_S4YJ2R\n:7U]neK[._nT\-ce0tKZ:uSL#C8L#v8L#Y8L#j8#v<Ly 2+#33254&+weu9<apxm86C?*#"'5637>54&+53>54&#"#4632#0xb14AI@ -54.).ie$G1rpE59B_0(',*%`sfPf "CsD "v^D "YiD "ohD "jjD "miDY%*6:%32673#"'#"&54?>54#"#632632532673&"r$; [@YtM[81#U/+u:>].TO/ %0P)#69 'P_hiYL 5!)22?{U &  >#A{"$ %132673+632#"'732654&#"7.54632#.#"6+(. ]$)C>:PC%"+hnye} -+r*G9-4]u(&!-04 Uzpc5- "CtH "vfH "YqH "jrH"CC""v+"Y"j#9&7#"&54632&''7&'"2654&%9W3Tql~}k$1(7M*F&49EErED 4"2M|y@ .%,]LM]\KO]?"#oQ#9#CR#9"v|R#9#YR#9#oR#9#jR2 !573#3#||||#ww{{ V-#7'7&546327#"326547&#"~H+K2}kKD+H/|t$27H'28G#I(MHp>F)IIkg*`J(t/`K.:#CX:"vzX:#YX:#jX %"v^\:&>632#"'#"326542daad27jAA54BY|zW^LK]]IM %"jl\#q$ "qjD#k$ "klD%!#3327#"&5467# 14=L$'FNA;KX_`'E%5/ 2-,I$%+8"&54?>54#"#632327#"&467#&575326JZ8(!Q4* mL $'FNB:MJI(0,$)#5:YI  6 *%:G5/ 2XK"M&  "#B,#v&" "vpF,#Y&" "YzF,#l&" "lzF,#Z&" "ZzFM#Z''H'G] #53533##5"&54632"2654&<<4‚ab3v4BBhBATCBBC7NyzO&_KJ^\JM_Op#q( "qsHOp#k( "k^#HOp#l( "lpHO!327#"&547!!!!mL("GN})]:}:G5/ 2-Q9}} %'-%327#"&5467#"&54632!327'.#" <8-L'(CM*5/#rt.SN/.?O9/^ *R4H#4/ 4-"7% ?{U EJEGqT'@C/ 4-L<"327#"&547#3#5|&(>HnMSS/ 4-GA:}}?#l,C#ό?#-,C&#ML:#Y-&+"YJ'".;$'N<%PC#v/C#vOPC'/C'*OPC'O;/C'O;OPC#y/C#yOU 7!!573楥oPPpXq}8Y8 7#5738888(O(&(O(dD#v1?""v|QD'"1?"%'QD#Z1?"#ZQ?"%3632#4#"#?:hV_Z3>NWbYMa:0D&#"'5326=#3;A$/ ۖ!B:p??&"%3632#"'532654#"#?:hV_:A$/ Z3>NWbY8B:pa:0(#q2#9#qR(#k2#9#k#R(#p52#9#pR#!!5#"&546325!!!&#"32pZ%C30C$+?*LPbbOM:};.$ק#4}}jIsr%)/%32673#"'#"&632632%"32654&.#"-o%: ZqBMv}}zIAs$CE3 9FF98GE7/` &P_KK LL*AkE ``_LP_u9QD%!*TLDO>Kfo^&GP:':D:Pre\'&!-04 S vn;>4.,0ZUku 1W9r1)('_U$%?732654&/.54632#&#"+632#"'732654&#"7.'+<17 ''wgm|d%,jza#)B>8SE$!+kc"$46%SaaVI 3iQf(&!-04 T TR y#Z6"ZqV$V#632#"'732654&#"7##5!#)C>7SE$!6MH\?&!-04 j\}}F+#327632#"'732654&#"7&5#5353-N $#)C>7SE$!6oEE]$b =&!-04 i rL]V#Z7"'OWV##5!H\\}}-#327#"5#5353-N "/EE]$b L]L#o8:#oXL#q8:#qXL#k8:#k#XL#m8:#mXL#p8:.#pXL"%327#".467#".53 53(88(M')"<2/9;25_Z6<`?6=!3/ -H;$6kI:4"4>7#5#"&5332653327#"&T-#@:hV_Z3>oL $'GN2!@WbYxa:0R8I5/ 2 #Y2:#Y!Z#Y< %"Yj!\#j<B#v|="vF]B#l="lT]B#Z="ZS]43##535432&#"LL)'&]Ai*? !"32654'2#"'##53533#6qhBA54BWaac2<<2]JM^^JKԡ}zN7TCBBC~O, .3254#3254#".5463!2#!#"3ٳoojjC^,ofDc1.6y5jIBU-2:^_"RS(B?#dm(?</D#Gj$DB(C9Rk;? "32654'2#"'#!#6qhBA54BWaac2{2]JM^^JKԡ}zN7}Oy 32#!'32654#dxyra<9uontK68m? "32654'2#"'#'76qhBA54BWaac2a2]JM^^JKԡ}zN7QO, &,"&#"32653#"&54632632&#"YdaWHYdJS$/ ⃈ywND|Ͱ,3po"K.""32673#"&54632632&#"#. r=5). bxyF3I/  -OZ-4aq$pA5-,!%3254+".5463!2#!#"3هC^,of_Tm%YYGBU-2}(B?#dm(,jhUC9R74>;5!5!!".%#";R %:d?Ij55::58E6%}')EH;0/;;? "32654'25#5!#5#"&546qhBB45Ac2{2ca_KJ^^MJO}'7Nz}I5#46 Op !5!5!5!!5!]:}}'}(3267"&5473!.#"#>32hMLjĞgOu6qvC%WefVק"[hmWo'ծ y'#&#";#"32673#"&547&54632l:DLfGBODLTY?'FQ:&r1)gx@1.4>;u|}os1/]ku 1W&J!!#"'53265!!?:A$/ }hB:p}&9##"'53265#535432&#"9S:A$/ LL)'&]B:p]Ai*5*'&#"3265#5!#'#"&54632632&#":(_mu[Pi$Z*hJĝkOR$/ uvph:}x^=8ئ-4pa%32547#"&54733W 8 *TBFPL򧕋 3j +:7.CzBCg#4#"#363232653#".5V/=:d 97 &;7(mzIb1Jd<.W'N5|H=CH(i{!BL5)732?#"&53 )> IX)/ aBNX:1-PNR,;$73#'#4632&#"DZŤ8:A$/ J=]B:pf #5333##c||}}:uCuJ##57.#"76327327#".'z)2;Hn  #!C(! N[Y_" z?9VXE{p# :l !#5#"'#"&533265332653l%H1r,AhWa,%07,%16H,&VV_T0#(<5#(<5 &33##"'53265F!:A$/ 'B:p?7"%3632#4#"#?:hV_Z3>NWbYa:0(#2#".54>32675.#"HwQ72KPYQ)+RlhMLjgONi,Kem:+\jQ7DpFHoB@WefV}[hiZ( "32654&632654'3#"[nn[ZomE_=7Ce拊pvZP`- 0Pd#I !"32654&#"&632654'3.9FF98GE!{}|G<7``_LP_B] N* 0P(8 &"32654&4632632#4&#"#"&[nn[ZomEW>pz*'+& e拊pvMXof),-2B3: 4 ("32654.#4'&'&#"#"&632632:CC:9D;y)yzyR;?>XG^UV]]S.L "32654'2#"'#4632&#"6qjAA54BVf|ad2=B/ )2^]^IMԤXA;p#PY3232673#"&546?>54&#":':PG&^ofK>ODLT\erP:D:9W1 ukUZ0,.4>;u|}oV_'()1r%(%3#".54?>54&#"#>32326Cb1j,%d|mgw'' 71<+#:;i3 IVaaS%64$h#$+ &"2654&"&5463232?#"&5=WW=CQ QXtW==7<7@WbYxa:0R=> 0P )<7332>=4./3# ,)')E=', -O46VZA=' ]g34&#"#37632'&%T32&#"#"/326?3` !(C!#  #12) ǐ #p{1.(z 4 B3#!!57#537!5Bktܡg}C}}C}3#!!57#537#5`3lpAq5naqrCqqCrq#"&'332654&'9#5!dRNqmd.;*#6li?h{}jo3232654&+57!5!lU 8 R^'&#`0ir4&\%SI9IsyPQlZq$$ u rP#'$"+m{l_%-2'!!7>7'57654&#"#7>)og<$ n95,SA2#0 $8\gtQM  G]A%F/G/=.,)GJ3!@,Al78@+ >54#"#3>32*5L,!H#]0/\FLf 6FJI>T&A8I%9'/{1&lY/\JH53 d8_d8""__n-833#3###535#53FFP\P2P\PpM#?'M#@'7#@cGPI#-c/P&5#Mc/C&#MOD#-1D&#M1?&5#McQ#Z$ "ZgD)#Z,("Z(#Z2#9#ZRL#Z8:#ZXL#j"8q:r#qL(#j"8v3:#vL##j"8Z.:#ZL,#j"8C7:#Cx % H##j"$q r#qj&$#l\ f#\jp#qY#q*'%5367#5!3##'#"&54632#&'&#"327$UUZ*hJĝ2O_mu[F5P}P`=8ئԄq1,vp,"&x% ."32654&733##"&'3327#536=#"&54632 3?>27EEC]r9lR#4i'C/dy`Z<`MI\\IKblChSF?CI,"~}\*#Z*"&"ZZJJ#Z.;$o#ZzN( '"32654&4632327#"&5467.[nn[ZomEMc9)L'(CM;4e拊pvծeh%4%2/ 3,*F#9% &"32654&'2327#"&5467.546.9FF98GE;o9)L'(CM;5gs``_LP_qz $5%2/ 3,*Gw(p#q#9#q#Zfyq"Z}2&+&ZM#='M#]'7#]cG*#]*"&"]fJB] >54&#"#3>321JQ,.#]0/\FLm#1Q>\*!;QZ-(%\1&mX/_JP4=D#C1?""C~Q#mb$@0v #vu#vY#v#v V#v #$ #D#$ #DOp#( #HOp#( #H #, ##0, #/(#2#9#R(+k-2#9#RP#5r#RUP+k 5?r#zUL#8-#XL+k8:#X y'6%'VV'7-'WWt:#7>32'3>54.''7> 54&#"tq;P2,I5V4,%L`V:54&''7> 54#"qYVo&5&!8%C$#  H[E *E  :&X[tfO@+,W+#" d( #3 s 8*D#Z+Cr#Z}K!)q!#"/5326=!5!5og"G26v*+v1tx()5!5!!#"/53265Z#"G2m@pl*+v&$l "lhDO+p#!#632#"'732654&#"7#!!!#)C>7SE$!2]:}8&!-04 c}} %6.#"72!3273632#"'732654&#"7.546{71\l#BE3  -"P}T#)C>7SE$!3hw?;@{*AkE -+DK^9&!-04 d{(#j"2\#9r#\(#o"2\#9x#\(&2l#9#lR(&2#l\#9f#\p#\< %"\l\& #"'53265:A$/ B:p[ % D(% "&5463253327#"'"2654&dab300N,k4M4BBhBAyzOFrZ`_KJ^\JM_}3 ;? #"32654'2#"'#4632&#"6qhBA54BWaac2:A$/ 2]JM^^JKԡ}zN7]B:phO" % F,  %#,">32#"&''7&54632#.&#"326r Pi4&<#1@-1G$ :LP#ye} -= L C.#5$55Q*  ( Z:C_pc5-* &c #"2654&"&546323327#"&=4BBhBATdab3 /$A:4_KJ^\JM_9yzOp:BNc #"2654&"&5463254632&#"#54BBhBATdab3:A$/ 4_KJ^\JM_9yzOB:p7N %2#"&'332>7!&54>3&#"ts[P"-  3EBH ]17%_PD+- EkA*{@ % H# :37327".547!.#"#>273>7#".'71] l#BE3 j -"P=Hj9 % !/ $ (;@{*AkE -+DP_0E37/+S954.#"#>3273>7#".'#"'+h25GG43h&05)A! Ph65gg5#f#6 ,0,65-N(5,$N^HD]N&&7#"'53265#53533:A$/ CCAB:pƇ"&] /"32654&#"&53326=#"&5463254632&#" 3?>26EEɇznR5B'C/dy`Z<:A/ )`MI\RSWV>_iTE?=1I,"~}\kB:p#"&U% -"32654&#"&53326=#"&54632632&#" 3?>27EEȇzlR5B'C/dy`Z<E#7 `MI\\IKbT_iSF?=1I,"~}\[r" %"327#53#'#"&54632#. r=5BXZ:Sxye} -OZCi(pc5- "254'7#"&5467373\M8#F] -rrX " ) %3+\JR[ & >%4&'3262>32#4.#"#".5467.#"#>2#!;"&E@<'-A$;?"68)9/ #,5& H*O334= ">k@$D# 8~%6P* ,G,%= 6!:\4! :&##"&5332653:hV_Z3>WbYxa:0RC4#"#4632&#"632#V/=:A$/ :d 97 Jd<.]B:ppW'N5C&'4#"#4632&#"632#"'53265V/=:A$/ :d 97 :A$/ Jd<.]B:ppW'N58B:p#5##5#5353όD@@}}}χC 3327#"&5C %.A:gp9B !#3!53#DD@@2673#"'#&#"#6323O!N v Nx Z#a#&";=33##5#".54>7**,-7%(O466{5$45#C& 3327#"&C /$A:^7p:Cq#"&'332654&'9!#3!0>Tqmd.;*#6sŌ\Nh{}jo32632#"'532654&#"#4&#"#<#C.j):A$/ NWbYMa:0^B:p?&d%3632327#"&54#"#?:hV_ /$A:Z3>NWbYWp:Ba:0<+ ##3+ÌRR$:%%$H!3#!!#"&6325!&#"327Dl7EB5j!U@II@S#C~P~CQIJPC4%4&#"32>=332>%4>2#"'#".{~x &##.& 5SS5-<< d;9f0N@#cKZT)' )&*,OQ:%$:QO-9Y8$DD8eH7 %>54&'#5.6753?1::0;:1urts\V]]O \DH^y|;n U;n!#5#"'53253nP0 {k7<{;'3327#"&=#"'5325 /$A:P0 {p:B7<{?8r%3>32&#"#?P0 {j7<{?&r%3>32&#"327#"&5?P0 { /$A:j7<{p:B?%#4&#"#4632_Vj7*-4jYb?%432#4&#"#?V_jbYM4-*7<6#'##!23264&+D=,#6N'z,#%*z;X03_BA; O&%4732654&/.54632#&#""'#"'5326=+<17 ''wgm|d%,j1b3:A$/ "$46%SaaVI 3i;:#UB:p&4#"'53265432&#":A$/ )'&F\B:pi&%##"'53265#53432&#"%S:A$/ LL)'&;]B:p]i*&404#"5632327#"&5f&') /$A:*ip:B&$&4>32432&#"&'# 73267&#" 46'2)'&;V+a;;F8J'9 i*w:0   m-& W;&-#327#"5#5353-N "/EE]$b ]j!#5#"&=#53533533#!326=':hV_??ˌCCZ3>@WbY+Ƈa:0" )#".5467#5!32>54'5!^#)/EvILxD-)#J &C*,E%&^-54.'7C324#"260.6$u XP;.*t k) ``q:qq3L6q#"&'332654&'9#5!r>Tqmd.;*#6s\Nh{}jo32#54>54&#"@/OJ+n2F-|#22#<-.@Po4f?R1 &1;S-%3#1BHI@,#4&#"#54&'.54632,@.-<#22#|-F2n+JO/IHB1#3%-S;1& 1R?f4o@, 5l#9%#y!R<#)72>54&#";#"2#"5467&546705,Zh34GG52tMe46hP !A|sg-56h=6#f#5&N]DH^N$,5(NVI"T%%"327#53#'#"&54632632&#"#. r=5BXZ:SxyM5#>)'- -OZCi( i)$5-<&( %)"3265&%&'#".54>323#5!I -A) U 0"Ek=( (P6 0HH- "q",%0&13 }};@$ N_<)33U&b !"32654"&463254632&#"#SjAA54Bd}ad2:A$/ 1^]\JOYhB:pX@,%4>323##5#5354>54&#"@/OJ+n2F-VV|YY#22#<-.@Po4f?R1 &1]ZZ];S-%3#1BHI@,%#4&#"3##5#5354&'.54632,@.-<#22#XX|WW-F2n+JO/IHB1#3%-S;]ZZ]1& 1R?f4o .Z)Z)ShK)Sh) uM)&U)"Uf))s fZ) b\w)4q4&qqC #5473ʇ,$Pm.%,i 356=#izE5CCg=Q. Ii #.=CC5EpI .Q=gt=32+53264&+t/BB/ B./B^  c=Qa[ D #4632#5254&#"`WHWHT7>Uq#ImB\MA)Sw9! D 4#"3#5.54632I#qU>7THWHmN!9wS)AM\B !A5% 56bE79E  AUL</43#'#3tIDEI}}/4WM-_F3#'#tkgNRRLdd _G#'373kgNRRL_aaL#53PPL;qy_=v_Cd#53HH}<qNFC=@F]M1&ddL1M13NX1^RKWRM7533!5udt겲ddMgM'cM 7#5#53533dtudtuuduudM<!5M3232?YG ?2)$G  :B bT#7!#7~FF:~FFMI5732>7".'SG,!  ),( JiTV0!.G3#FZM#GD0 ht):fO)XV)c[)5s) #!5f f #!5!5fV $f #!5!fV _f[ #5!5!fV f$ )5!3DVff9g33!9fOgf9o#3!ffOog HKZ4<!&qqRI\(4WM&(/W BU BVL8m@}^/}'^^/{']e]kyo qAIA 3## Pff #5#5fPf  :0!533530NvNƉ;;8/!53/PxP\\#.'>7\$$Y 2($#Y=+$N ,&!+_C)_v\Ypoq!5OO]k&mlmj4}#532654&#"'632,, %(04F@ 2&$3Nmbp_Z.L~[L&Lb3#%3#~FF~FF]J#klc] kI:u +>3;3>zOQ;C=vx3#5#53ff,Mf3c!#5#0dhR +532654'7R3X 58c(& &3#5#[S@WD!#7&#"#>32733273#"/g??2)% `. ?Y Dh:B 4O0 =#oF#lloER#ooeG8 $~~p #5'67'3("?9B7"=Ԩ"7/JI0MJ=-@@-=J %1>32#".'#"&546323267&#"2654&#"028;*63+::0 9"00aC$'BB'#D"I)8)4P37#7#a$E_'Pe_ ^>73"&'.'326*L0M$z|y$M0L*"vvwtHB  BH 9b,TT!!Z_eTT[#43232653#"/&#"H,-) 6*Q#G0?E!!0 mE&%$5#? ^HUE 5!'7'77>/6FF6/>7L2/**32**/19`DBQ:NHCQ?UL?Q:^RCQ:cXCQ;oFDQ:iXGCQ9lPK:Qi< PS<Q73#"&=3  ';1'[o  *,A,qR]_!4XM  #7#5!#5BF4XLX Rbbbb"qv#5#l#y;'v'| :#z% #z)>I"9:$H%!#!|S13' *Ϣ'}X"($=M+#2#".54632>54.#"!5!f̚-[cJ17sb&FD(,;7lkxť2MQh3cD7X4" z,.#3#SÐ~$'###3673} ZZ!93 a^If ##33fQH !5!!5!!5!888>HHZ]K2b#!#!bb[T+#!22654&+T '>mG'*;M4/Q>DM :A..M229'h !!5'5!!02uqj###5!jj\[3#3xˬː֯'Z3%4>753#5.%4&'>2DX;2CX;8RACPARU\Fn=( OOxEm;' GG~CY]l\XDKZ#.'# 373# ²,'3`",##5".53326571JJ/0CO/!8RIAq:W3 1[?kA8+1H[4&#"!53&54>323!56@ye^u{vRhzxlikHvzbbiҜ^zuK#j0#j%P#1#58#7"9"]EP4&#"32%4>3537#"&'#"q6@xvx 2EA#]6{"%D,/ =U @]?c<'/)Wr)B(4>32#"'#"32654&+532654&/[Aju=w~gW1<)_+AJ:2;07s>(%-9,$#5&#"'4632,ć & 154^~L6d*<D,%2>54.#"'4>7'5!##".+; 9)9H!,<'q "$#+O_>"@J7$x0/,0T>;]4& igrL-3H)Om: #7b#)%#"5467&546732#.#";#"32674cH !A|s E`26Eh34GG52hE6*89,5(NVI:9(!'6#f#5' *!7'654&#".5467#5!"7BB7"1|+/C>6!eXHxX-7 9.=x-Y %3Q2sIxb)l8$'632>32'4&#"#4#"7#=U,n]!2q r<pt%76 #2#".54>.#"32>7J@H3!8gK=ZG$!3H:7n:7$01O[Xh5$Sj[O1g[sp\!A<.7327#".5300M-0.,"B. #'#373'9!#'.#"7632327#".' z )11#  #!C(! N 4 z(.2{p# 672653+"'#"'#3(< $7@0O:/7.547#5!#"lBUP:)4:!2x)/>`P+-E/%="0ToA1,81#>x,m -T3232654&#"$7JE#G82SV.#EJ7$tK42ON3()Ag>($Os:'323!#"32>54&>a;&3TW0 ""s*~$6 @A'*K %:NH%Pt;~AV}!45JZ !E1YL"&5#5!#327A[= +O TTrr }%#".53326534aHMe2(CB'8N? AL7HO67T(<!!%2654&#4>7332##5"&APPAT#:,Qk[Em"BnEpRB@W8`A$dH[>Z{6`M.9M1#"/#'.##563273327M*'P5L$:L'kz 3ao w E3t%>53#5.533~55rs19l9N$lxwm?J=!&%2>54&'7#"'#"&546736=3!( =BD@tc\44\ctBC@@&-K!e38!ToD)7BvAAw?0'BqU9S:%1"j9""j]E#?"h E#I F3"3.'23##".'332>7!4>C x &;(CD#5U78U0 08$!&3yXW[Xz 53I:)z=`Z<#&8WK1]X-YB1CU9B,+$52>32.#"#54.!6*5**'I.J2V" ,)% "/Jl  ,D+W+2i# ?[_)6bDS3##P"Pj7z /)%32>54'!326=3%##"'#"&5467#5#''A@S+ #./)BG%[54\jm.).?&rZ[q(xf`Jq<DDs"q3xU%(733#"'5326=&'#765446{d%D=)"G2$ "w\_m>\_?yF&*+v9noY'" ?U4&27332>7#"&'#765.#"'>;V yd(YP;4NPyF.="T/:MPoY*25L1> -6-/2#"'3263"#"&54>2654.#")|&8LA"J5 )ND-O{1QW05L))2M)(-Kr?( % {=)Pv=,O`1H#N_4I"(a-+%#".54>;#"3#3-!qL$IQ>)~ptmRs#CoRs#jo\253654/###5!#3R- &8J,F32#"&'332654&+53254&#"-&:QF'?YTLDOBGfLD:9W1 uk]/1so}|u;>4.1@xg)1rR 3#33#薖!'R&rkR 3#33 #薖$##5265!#"3P33& \2UdLB#'R0R+$2R!#!# ߖM\'R3$&V7!#733HrY$d-<!50# 4>;5332!#'#";2>54.+y"%94E--E49%"';&!4'!'4!&;'^4T6( [[ (6T4^`!D.0G%%G0.D!;Rj  %33#5!3 nǖ}\<h%#"&53;3#ҼY0 jKC {'R )33333}ۖ\\RjW)333333#x\\! 32#!#5!32654#dxyr|<9uՄont\}68mR#32#!332654#dxyr<9u'ont68mR 32#!332654#dxyr<9uՄont68m">32#"&'33267!5!.#"% ~TFN_ #bI,>! yα͓}FLmc}T]2R!$".'##33>32"32654&@kF43DwIZeeZYfd%>GP:>^F3ܧ|芉qxr #.5463!#=#"3ݳG_yrB<9u }cnt'}68m$%D*- (2#"&57>7>?6"32654Ja|v+h+&S+hBA54B%},\ * 3OWu]JM^^JK<#%#!!2%32654&#3264&#'N6 3J$)!+.^(""(^d*%#,30/,$B KaqB<##ЌqU%3#5!#53>=!3#DRn]n>%(ñe}}rU:3$%H %#'35373#'#Aձճ(  %%732654&+53254&#"#>32#"'+<71#/\^A,%d|mgw,;1bC"$ !nGIVaaS@"%J;:#<+ 33##<ààRR<+&k<% 73#'#3Ȣ_!###52>5_er=DZ(L93<^ !##33#qHabb)G)< 3353#5##<ˌˌ$:%R<!###<ˌU<&@%S$ %F###5qUq %\$&!+2#"'##"&463236"3264$"32654f|ad22da{gd22jAB45A7jAA54B%XXYYu_MI^]^^]^IM [<u 33333#5<ˌVnUI}$3;3#5#".5$xv&@>$!B/<: )33333:UU<)333333#CxnUUI%#!#5!323254&#%P97lkumT0)47!qjF"#< %#!3323254&##%P9lkumT/*47!jF"##< %#!3323254&#%P9lkumT/*47!jF"# %.#"#>32#"&'33267#5w 11+- }eyxb .)+: @<8-5cpqa4-=8q<,%%##33>32#"&"32654&.ffl~zm8==87><mxxS^^^MQ^4>3!#5##7.55#"3.'N6#,=DTz*%#,30X;AqB$"Ci$"jjS8&"##53533#632#"'5>754&U2;FF:e :7 F`^1l4<.7h::hrW'M5>wM/>~^8+<"]N$ %%#32673#"&54632#.#"3c :+). bxye} -+11 8=-4aqpc5-8<  %V<L"j&M^#2#!##5>=!#32>54&^uQ_ 'T?.hhm '?Q<:Y3JquD|nc<^2#!5##3353#32654^uQ_ˌˌhhm3$?Q<:Ync/6"##53533#632#4U2;FF:e 96 =-7h::hrW'M5Ad<%#]<+#C %"kj<  !#5#333eň}}UF(F<3H<$ 6632#".#"!26$¿f iSRk q klPծcrsbOcuv$:% 2#"&6."#326.{}CbCC10D% >GH=O=II$''#7'57'57382C58=C@CCBBzc 53353#5#zxxxc98];3>3C "#4>323".+;yFd;Jx>G (N;s(*C^(!(!_e&s'sDy'sy's3'sF'sf'ss33Si "/<IT_35>5##546737'654''&5477#"'7327''7632&#"#"&'335532#.#p*F&@qp*F&@O*)&- O*)&-"O))#$2"-O))#$2"-#;38 *$8;38 *$<;38 *$8;38 *$O))#$2"-O))#$2"-O*)&-O*)&-p*F&@rp*F&@Rj 3#333#5#"&=33267薖!nLpL8 D!# 8LL9 B#< "&=3327333#5##LpL8 D@ àVntà 8LL9 BBRI}R32654#3#32#!#5353<9uOOdxyrFFX68m"OVont+O_?73264&##!#53533#32z*%#,'N6PPOOtmqqBD30OEEO;]R 654&+327'7'+#!29< -8/89(5Bry(86-8/89tnp<&@% %654&"327'72'#"'#36AjAA578f|<;8<3:d22(9M_^] 78N{vQ;8< XPYRR`3#53!j<##35ЌU~] 3###53!!󿿖NN}:}"} 5!#3##5#5Q\hhBIqbZZRT_3#!!32+5326=4&+nn%.@@.s}d~``~}700732+4V55,,\@6#>>%!69 2DdO8,2$}q %O85N' j*%3#5### 333f,eǤlz_ %3#5#'#5#'35373InEձe}(*64>32#"'53254&'.'332654&+53254&#"7&:QF'?Y}NMC('L!-rTLDOBGfLD:9W1 uk]/1sg|.G,3 /2!2 zi;>4.1@xg)1r  %;%#"'53254&'&'3326=4.+532>=4&#"#>32 ",=$OMC('L!++<17$\^,%d|mgw,;,C$/G,3 /2!0"$  n IVaaS@"%Rj%3#5##33dqFۖl<%%3#5#'#373VnHe}R%'#375373#'#,,OrȪO++E4A<%7'#375373#'#$$O/4O+*~"7,> 3#3 ###5353MMƖHHOKO? &3#73#'##53536633;[;'w 3 ###5ۖ\} 73#'##5FqRj%3#5#!#3!3xߖ lK<u%3#5#5##3353Vntˌˌe}R !#!#3!!!ߖ K}< !#5##335!#ˌRqQT!#!#!32+5326=4&+ߖMnn%.@@.s\~``~}700732+V55,,ˌ@6#>>%!69 2DdO8,2$}U %O85N', ("26=4'&#"54632##5.54632@ Ydi}EQl}nw  #?,ȃy57aVCiEi`gĘΊy$ % *%"2>54&">32##5.54632#.  nr5$;%BFgOp\cye} - k*22O7TF`dspc5-,'&#"32673#"'53254&'.54632YdaWFTxNMC('L"- ⃈ywLFt 0F,3 /2!2ɟΊy$ %'"32673#"'53254&'.54632#."r=5). hSPMC('L"-ajye} -OZ-4Wm /H,3 /2!2vpc5-jW %3#5##5!#xHl\}}  %3#5##5!# Vnte}qq y#33򧕋BC8%#33oԧwmQB y7#535333##򧕋ȖhB5hCG#5#53333~ԧwmhB5hj|%3#5#'#3738D0޲loj %3#5#'#373Cn:[\XWe} j#5!#!33#5!ο !n\}}!\#5!#333#5!srsˌ`n+qqI}Dj!#5#"&53;33#\n@.n~`07{0P!#5#"&=3;33#tb\*$dVnY]##I<h5333#5##5"&53,OVWOh3`WY{'UU^*5<7.=35333#5##Qk0O33OZT5 cgSFr32#4&+#3ܞn@.~`07F!#54&+#332*$db\##Y]*0!.#"#"&5"&54632#4&#";>32!32>7LTFAX ~uYJATn%!^~cZ,>! FLLyδYwQ]A>%0/7|2 % 13.#"'";>32!32673#"&5"&54632#4& ,'$$!Hae}<6). bxq\I1,FTd+% k=]dpcNQY-4aqG\6H:.  <C%#"'53254&'.=.54632#4&#";>32!32>7%!.#"2;O-OMC('L!,mYJATn%!J~cZ,>! LTFAX>a3# .H,3 /2!1ʤYtQ]A>%0/7|2FLL % @3.#"'";>32!32673#"'53254&'.5"&54632#4& ,'$$!Hae}<6). hRPMC('L"-bjq\I1,FTd+% k=]dpcNQY-4Vn 0G,3 /2!2xG\6H:. R#' *#ksp #kRT2+5326=4&+#33nn%.@@.z~`j`~}70^075hVnterI}=DZ(L93RT%!#3!3+5326 ߖ n%.@8Y`~}7Y]##Rj#333#5##n8D8<!##333#5#qHabbVnt)GI})R"$k$"Dkt"$j&"D jt%##!!!!!!#5SCr}}}G$b%*6:%32673#"'#"&54?>54#"#632632532673&"r$; [@YtM[81#U/+u:>].TO/ %0P)#69 'P_hiYL 5!)22?{U &  >#A{Rs#ko$"k^,%!326>32#"&5!4&#" TFAX7 ~dY,>! FLL/yαz2"$= ,#j""jm *#jup #j y#jq  "jsyq2RZ#qr<+#qR#jr<+#j(#9$$:%$#j$$:#j%"#j "jo Z#q} %"qr #j}%"jm #p}%"p~<h#j$"j{RjR 73#5#!!nl}< #3#5#Vntq}R#j6<#j y'#&#";#"32673#"&547&54632l:DLfGBODLTY?'FQ:&r1)gx@1.4>;u|}os1/]ku 1W%+%3#".547&54632#&#";#"326Cb1;,wgm|d%,^\$71<+#:;J%"@SaaVI n  $$9##5265!+53265"3P33& 3X:_Z1\2UdLB#'(E9!%X!+532>55##52>5hkaF%0)erFb i@=DZ(L93+4$&(%T :ZR#'##37'373o%ۖbo'? U"=<%#'#'#37'373oKYIo ZjUhA gF~%7653'#".53327L )LE_(KO:%#yQX=Q$$8];F2#54 !!#4>@NKO;%T%;O$8];dd};]8$- "4&#";#5#".54>3 3PA.B$<^7&BzY'n1<1*ll%=BK-Nu_2A!4 #5476 3#GRRGmmx@JJ@x}F ".53!! =3NKO;%T%;O$8];n}FF;]8$<4%32#54.#!#53327>54.#"3!&5!2H.42rf+W1(@<1J'[H"B/ .71 )*F)7O'(4"#tgF %!33#rԒF2#54 !!4>@NKO;%%;O$8];܂};]8$FH 3"326=#".546;54&'#"#4>;23#8"'MF:v?\2c2I#*3SU,r66W*SK*:6 /G8%z)760% A_0g2h- %2675#""&=4673533#3>7_6?xwvc`e/Au`:6}|jsg}}fF"#3632#54&0A#FSInE*A4)# ";Yc>``o]F733FFE53!".=##332>MoD'9=^'"@ 8\fHY[p]*S*?! +4&#"326!2'.+#"&5467"&=7z~\^V[|i,p  "֔K=RB!72>=4.#""327&ϑ{]P%v5H:1I$//1c#3B$+ 9M SZbx3b?)*6,%---A!+.)L &<Hh !<23!4 #54>5PKO:%m%:O$8];};]8$#%!5467&'767'.'c^0^ILg5ȑ)6p o =8i>"y>hR"F".53 5!#PKO:%m%:O$8];};]8$#N/%#".5332>54.#!5!254&#"#432Nx&HK6# 1%%0,$l@3cHj6Qo $8\;(9 5'$.Z,(a(CE$<@7 ".5#5! =3PKO:%m%:O$8];Z});]8$-c$'5.'"32>=3#".=46Z"E'grL?+"~?h=*Nglg~OIQ6#Y_a(;H;jaA#4.#"#4>32 $C51=" C|ZGlD*b7F76H9cOu^1";Xa-d%%5>=4&"#54>32Z;u}RxI$CnBPx;gnw- xkUM@I32%!  C|ZY{AbFS" l&I2CDOt_12`tA :654.#"32#54.#!#5332654&#.'54>324&AC&/G&`.JSrf>XB I. -DqG,UZC+HU;&9R(#:6PN .7*$%)k&OVA+)?iCgA23##4 #4>:PKO:%mm%:O$8];};]8$A3#".53326A|YIlE*A5cE?YKsa4 9Wc@Z2>?\-5332>533#5#".-=,.?#Z<^[}Cpo&.>"87,2`u$84>32#'.#"#".=332654.?N|KMs;#JF.*2Oa`O2z]F'K5?Y'?QUQ?'Fg2+9K.:2"' /V;h4VV-+*3-(!-PF2#54 #4>?PKO:%%:O$8];܂;]8$(X1=2#"&=332>54#"#>7.54>32654&#">Om3=T|~J>*-+8   5k)E0/DH,0D'CA&Y/?xj~q 2A 4$m 9'%BB(&1(/.%.F33##F#'04>753#5.3>54&'#.AZC#DXC/-AXE"#EYA-/%-!PN05RMjGrD1;92EtHGsF4223Et )D*Tb-M2Tdx!#"'!!#5#53462!"32654&tvL5}WW4BM425G 9__+g};3.'854.mY˥Nb8О6?,wd%FF+-=6fj8dX #8fCs5kL=_8$ #&/#"&=3".54>;235"4.#25ɠu@Z*#DM6xQt<1,#u0'zVM5 635H#+HL+NiXa,;~c=R 53'767$( 1Ti)1( P '373zj3G32?#0#"'7>t#< )+08M #@)*s5 3u)_C 7\/$2#"'&'&=3#3032654&#"#54>vJ<8/ ^ $+*a+/L84@ ,/B7:63Z'?3eR3CfQZ<-%3#5#"'#"&533253326:Ji.?\Z`'f!"25 !,EEebw33@5'FA&$%3632#54#"!!#A:hV_Z3>WNWbYfIa:0]&p% "32654"&4632533##UjAA54Bd}ad2NN1^]\JOYPA]XA7r%36323##4#"#A:hV_NNZ3>NWbY]a:0<!#5#"&53!!326=3:hV_WZ3>@WbY5]a:0:#&u% "32654"&4632533#ZjAA54Bd}ad2N1^]\JOYPg]XF %/#33#o^ʌ]w]A&$%3632#4#"!!A:hV_Z3>WNWbYMa:0?]A8t%$.4&#"#3>323##".546;#"3251A{P0oh::"5"0F#EGF (<&8{j7<5Pnx 5L&33E_x3# #"&546;533+"3267"2G;"vyff'6 6B@+In<% qȌ0,5GLJF8 4#"#3632#V/=:d 97 Jd<.W'N5F7 3#N]F8!#5#"&5##3332653:hV_NZ3>@WbYya:0R# '"32654&';##"&5467&54>?.9FF98GEUcta{}h]&P``_LP_ 1v=Rq+,P A8$##"&5332653$:hV_Z3>WbY5a:0RF#3632#4&#"Њ9I;S+)+p(#@M2I8-#9 /"32654&#5#"&54?654/&54?.9FF98GE* > :irZFk}fk :(:``_LP_%  >{$;MP   1A8r%36323#4#"#A:hV_NZ3>NWbY+]a:0L7!&'7!547&'767 qfUpҢ)!(5Di_RQsPFF )yGr>A$#!#5#"&5332654&/&546;"$:hV_Z3> .'&(@WbYxa:04 -]  &%3#"/7326yDW.  Dej {A$!#5#"&5432&#"32653$:hV_)'&Z3>@WbYi*{a:0R8-,4&#"'7>323!!"&54>7>k03&!j %)<#7Y3# k! 0h$K?%\ $5?1Ax DM$9'0> 5A0#3632#4&#"ˊ:Rʊ57:%%0%MAD8t!;#"&54?654/&54?Irh!^,y/LvP:x 6F- x2(, '"78(%3#5#"'#"&533253326:Ji.?\Z`'f!"25,EEebt33=5'F#82,!"&54?4.54>323'7>54&#"-b%66%KmBq;DF2( */I45):Tt+!^4*2T7Jf*mO}>@/ (D&JH=/5!4A)%36323#654&#"#A:hJpY/x7'3>NWb; ]M +:0A$3#5#".53326;Q/H*  26:%$0+,=&QJDDA8r3##"&5332653$N:hV_Z3>k]WbYxa:0A{%!#5#"&5332653632#4#"$:hV_Z3>:hV_Z3>@WbYxa:0RNWbYMa:0A8$%3632#4#"#A:hV_Z3>NWbYMa:0(8#0#.253#".'3327#".54>"2654&"H65dF*C-"!2u4BNs87tU,GF^FJ0(!/?O0! ## Y$PPNVzL[cJM]ZPF>3#l@\A8{##"&5332653632#4#"$:hV_Z3>:hV_Z3>WbYxa:0WbYMa:0A8E% "32654'2#"'!!#36xjAA54BVf|ad2W2^]^IMԤX])PY#;+$4>32".%"32654.#2SX/x"5HGLGI5! '*)(4K)(Ry@FmB++Ai "J53I#Oa1H##8&-##5"&5335.54>;654.:W 7,w{AJ8T1 6QO!x67H (  "1V5rl_ 2%3.2N)vY'7Ar%3#5#"&5332653$N:hV_Z3>]]@WbY5a:0Rq}* 727#"'7#<8'#I*-~M<E+}&&z&z&&]e&W~&w&w+r}3#+RR8X 4&+532#}Q4Hm7m.Js1KGLX4&+532+53265N7<_8% 7lGź5Pm*Ns#4@5GK2sN*>S%#54>=!3!*/*v*/*eek7K!6)B :N!3PeX 2!.'#!Z>c8% .#X'7D3X"2 CyX4&+##5!2!53G8EHH[,pq1CsdsO8X3O IgX.#52!530 M`Cm(s,aCsLaX7!2#".%4&+>5L/YN/m(XV8S2z 8''7 v3[:mu2\,L !+*"<CX%653573Z^)U9Øq%YR=H<Lx$H8NY;#".=!#.#$5A$9$x #-<&#s?,]7  OUX";#".=!2#!532=4&##5A)5'Ik9<\L"X"s =()EK+Eb,swL+8 X #36753 _Cɞ7 SZ ?:M=^X!5!3>=3CFE Ǟ$0is3P8WX%>75!5!5'#U>*\*@0t -@s1t;?X 4&+532#P5χ}m3Es~dLX"+"&53326=3+;2653*KwEO}D%0F9@R-O8n)YO3hO#5L#*3X>O>X4&+#'532=#5!2#"N7U!.H6$WB.{n3D>Y/tvsyiOX3333OvXXIX33ԋxM9DXIX33ԋM9D9D2#'5*8(ق2 #'5!#'5*8(v*8(ق#2>32#".54327"&4632,5  ,'03 3-6-N&;! A/P,<:0&&f "&463253"00"$..m0D00D0pp(10:".54>32'"32654&".54>32'"264& &0! ' 8M 0'#*&0 !0& &0!M8 "4"#v 8&2M8'8 *,#  9&'8 8'8M+#"4"6%4>54'#"5467654+532>76753###"L%+%#eM .-R2/F\-A -2U,0,7B~qY6(/Vz:*:GC@h_;E*H(pP"0,,62I^!pI.44A8>%4>54'#"5467654+532>7675;##+##"L%+%#eM .-R2/F\-A -2U,0,7BdpdqY6(/Vz:*:GC@h_;E*H(pP"0,,62I^!pI.44A8;@.#";!#&#"32>32#".'67&'+##53>32375$>?'\|;^> $I"9 'J/375#".'67&'#5!3.#"#46323##+&#"32>32-kl'J/?"m^yglog;^> $I"9 xu{ -4%(P5^G5spbY0Lovp&#C """"-;326767!%# #53>3#.#"!32654&#"nMc,5 nK8^w]UX/DvK`'7#C@Y a\"W3O-;2]4)2==+j#E^Shp@]F2&Ysp4@*,1B"h%463267!5!##'#".7327'&#"aZAA,jLD+tFk|4Q. n@0UV<1A0=Zw45_Hppp~$4@//;w5":3275%4&+532353##5#"'> 8hX3$,s#apq>B\@Qp C,2Eo5~^+\W$-2654#!5#5!!32#&'#"&54>327&#"@V51iOVi8,5u&WgPj&K7"/3@5F/).(,4ppKWG.W0l.0BT@-.'14632!5!5!##"#"&26=! b6:e%"n1KR3K.ppTh#%,S"'-7467&54>3!5!5!#!"632!"&%654'# 0& )yl)fGaW+ٓ^o@@",>-"2 Opp  7)?, 463235!5!##5##5#".N;@~pp" A6A3pp 17"#"&547&'#5!##".#"&5463232654&'327N!4gC&:pWcxd +2*<"'*5*S?UW,Q,M* "+UppJB7URl'#)#&6-&9%1V(;53!#327#".'.547&'#5!#!"A"ypc%YP/4B=(vp oȁ.1H!kTR!H1X32"&'>7675!5!##"$I8%(^t.Jz-\7_&&#%[}LDq,Lpp@\-7 $"326544>75!5!!#"&k@1KhX1_?@`2aL=AF0:7*@51 EppE 24@*h+474632654&#"#"54632327!5!##"&7327&#"#_MkbPscF1NoC$c )s G^avmM 5?JY<:F` .6C *Wpp  =k,OJ,T@8$-4632!5!5!#!632#"'&'#"&254#"^28ՀMcHX !G3u1#79 +k3G)rpp!7+VG;,/@m# '!/%2#".'>325&'#5!!&'#.#"32>_E$Sud"?Z3=?1nMB:k"+,+4O`  ] #U=5K-$)J7!5!##".563232654.#"#"542# eNhwa8~\= $=>2$<< Wb1(qquD;dWj$0- !B+*+ GB!#5#"'654'#5!#!353275JpVX}v8qz:JC_j'l2qq0"g ?<1V$%2#".'#"5463235!5!#!326L YEg:$ Ab1:yH`9y$R#4ND*82P.pp[Q/!#5#"&'654'#5!#!3270pRWC,8e{,D@>^'O>[!pp "A=0+74675!5!!5>54.#".!yhk{?()!)=4Jb!%!9 M\pp\N)B()j-#%8 U0#.j+72273##5#"&'>54+5Z=UKH\xpTWOP$ .g"$b32>7!5!##".5463232654'#"&%&#"32'@E%[M.eP@3RU,qjN5EcsL\066U9JR)<;#%!pp\V=R54+53233####"$kjp1!OhC?f6spCpMK87654'#5!####"&32=35A`)qjSBP5OG"ѭM(ppD@Vvu#'[R@".54;5!5!#'2=#"␠g2z-*D\ g=upppU ;$DY(+7467'#5!#5>54.'#'.%7#XU;:WU~0C! * \,Z*RfI{ZppZ|Hfp'&#5  4$vp;'*47467'#5!#5>54.'#'.%7#"&5462gWU;B.-JzZppZ{Ifp'%$4 7/vp- !,,B,'4>;5!5!#!"3!#327#".'.+?3;zEp?0[]2T448.G&8 Hpp j-[&<2A&74654/#5!####"&35"&54>32_ kp$@$.#0$" #10i"%(Sppa+[G0#!) 1$#074654/#5!####"&35)^ qzp$>%.i4"&Sppa+[G4>;#"325!5!##".,!;&JB1vOIE"9:Ci 'p )sF[pp1G%J8T 2#"&54$-"," -"L7##5!#pl`pp.#"3###53>32$>?'qqowwlgy^N.LoppupI23###53.#"#46glogg(>?"m^ppbY0Lov b #"'5327o[\]f\d]((r)'b #"'5327#"'5327m]Z_f\e\l^\]c_d]''l)(((l('`#"/.'&5432 )*J]v#J< )-45?j#46;2.#"#"54632#"&#"+"7( A^J2!1 $L@.a:94S '! @_2 k/ :l#"&'&+"&'&54632;\V5  $>108#<  (30#  %4|+#"'67&#"".'&#"#"&5472326322  /( F`  @.^@K .?&(7 9  3#F<-& M/./&#T LD 4 w%8  @R+73275##5#"'654+532353#".54>327hqq &6&< *"&5463246;5!5!####".73275#"#00#%/0&o\XrHypaIt8qU1$A@11#%/0$#1N\pppSo4-^$ '4632!5!5!##5!#5#".".54>32N;@~pp" T$%" %//A6A3pp  * 2#"1<2327!5!##"/#"'#"&5463232654#"#"&5463R IFkKP+#+ />&9$ |Ge8I\rA7UC dppBB-Y%A*' *!8-= %#&3".547!5!5!#!">32'4#"32"&46329~kF ` =Y@LdlG^P0T>*#%()$!-Mr?OqqG &1*M?@O1&%6,88!q 4632"&"32654&!~YZZY0Po .8*Qoo@YX~XY>qP-H(oPRoQ+4632#"&547#".%4&#"326o-PM.5e [=6V1! ;K9W84,iRo-Y=@58X+,u4I,!/9+$4&, ( .s/%2>54#"#"54>32#"/&+"&54632 HZ>?CGfO[D"+.-% }/,HP4J?/&d&R75'0OSZM5,;$,%)1"*!pA2#"/.546322654.'#"&546323654&#"#"546;C!3*m b.12'%XY5z V0=O?~D'?bOD.? %?'$! "0 0&-B!  " (2 *1"1D$/"&54>74&'.5473254'5%"32654& $op0 "EM?O=AbO/& >%/p BggCp/H* $9NP /B!!-" 223273#5#"&'>54+5L=UKIED^ooXPKO;)m;738,R4 ?p.BK4>32&546;#"&#"#"&#"32632#"&#";2+".54?4.,GG%]MI109:*1*F !1BrR)RH4] ;9X'#,8R* /E#+*:F &%4%3$V&) ",9&!!7:1('< %2#"547>54&#"#"&54>d~ =-l23aE!2-&Xti3FHF?+!^f9K( %1R* 4632!!2#"&'4'. \87<)Dv9_'1+0^/&p *MW>*#ԫ$  C"3#!32#".=47#"54>323&54632#*./&>'!4$,-FrHL:b$b]>$5$3,8!XI.U" .5 % )NQ=&"6Urn9 8(=0(-0?K$462#"54654&#"#"&PP9 0%$19)7GF8,D 4!)("-Dv".543232>325313 6#0  ,&Q C0L+=**Q%9!pp%*%#".'67&'#5!#&#"32>32375d'J/ $I"9 o-k -4%(P5^G5spp&#Cfxu$1&#"#".5#53>323! 7673254&#"H)e]u_WN0KL MM 1N2hG+)puBH^Sf>cVppGB8@%6= E)O=Xbiv".5#53>32>3#".'&+"#".'&#!#".547#"&5432"3265427674&#"#32F9\T7!%"*=kDpP&AE|P/((EeB^3 #/P[t( $/&F3Rc  p*0&K6;E-i):&gIu|o@ZB0M8A6QY%8"HS@% $ 6jojA99$iQDG1P.,*N 3.%NB=K7?(P)5;<$#"&54733254.5473zxi 56m/99//:9/{i&'$$8Q3N/'#)A+( $:(&-9[<I"%#".54675#"&=334&#"32Io6]N.g~)xLZI87QM:lq/U8bj*U: mY7:758:<\%1%#".54>?4#"#&546324&#"326\qGzV-Q^96"$xmPSa+*O>9VQ?=Ony*eI;Z5[b(" N\XR<(%C<>GD8=B<< '2"&54&#"#&!"5&54632632'4&#"3261d! M Rrx~)' ERdqdM/EfZyw .,.3.-,4Bt!%J)6972/Kv Re~9:vYbq.C=^?<<#!%#"&54733254&#"#&54632#zjpB7t54.?z]jv~n(%&8J5C8-[i<&*%#"&54732654򂂮&#"#&54632&}mpAr33:$*=.;7xf\bbn{n)!&:KR<:/d5%.9J= er^Wj,"<'2%#"&5467#"&546326324&#"3264&#"32eg .7C[yPJu#EaXqM'!3* ",1@0/H@20bnsd0.UBM\YH"+-z#" ##`=<.1@PP-%#"&54632632#6=4&#"54'&#"32765kmjdGK`ljAAw`+41$313BU41<C!#654.#"#&547>32CQxG//GxQ hN}ȡ )F-///E)ӣ>*jQG><&#%#"&547332654&+532654#52&ilB893&*#(JYs_bjj)!&9LR<-#654#"#54&"#54#"#.+53&54632632632{ !Bx4x13 [ivm[<$>=W7(UgZWI,+LK&1\JZ1CJD;]#yy"#ypFo5G!#,/\B#6qPZ|????h<&%#"&5463254&#"#&546324&"32j_`1?1,02}Zcw8Z<<-,iro^_t%-A=0/WbtcZ??Z?P$(%#"&54632327#"&#"6324&#"326$_bSPw7&%3"w>MVq=,-:9.-<]pq`\OTx%!%|T,@>\>>P&#654&#"#54&#"#&546326325x+%&!""!&%+x5l_N9:V\efng8(73""38'epe`w77z]<&2%#"&547332654&+5232654&'&5472&hmA92:,$#$0W! 9?=(^-2fl~k)!&:K>2%2x 'm;,_H/%#"&54733265#"&547332654'53273zjpB7<8-0^:>di##gHTjv~n)")8JI>j,76=:"*,VB<6%#654&#"#4654&#"#&54>7673632632CC#%*x 0)+>>+>\HX1x3Nd]W(>>|e_{f%W<>YS&,]=:f6f """."".`9>rOjgXgcf[](,B;DH32632pUABI.x5#8:x-4$ /J>U o9!%5AXWC!'!I:$ )*.M=<;D%2>54&+532654&#"#"&54632632#".54734&#"2/B $)/0$**$%'81*-]~VcG=eKl2/5*5_Z5cK3#$23Fd/,,:x:%$,(?DDcZSdGGdJ5QO?}v*R9('";7F33F3<#"%#"&54733276=4&#"#&546253#zjpB7I5 %+b6xjv~n(%&8J.,-#1&GT6< 6"#4654.#"#&!#"53&54632632#654&2 #%x &!0(V-F.4 tHJ_z`Q@@gTj16- ) f* #1(K4 -/<i[_v@@zUPSdB,<<##%#"&5473326=#"&547326=3#|koA8<83OTc!$+X4xkum% %&8JI>)bT?B6B-<<,y2I&.%#".5463254#"#4654#"56326324&"2IuArPgE<89x5<=6BD)8TRTLjLLj^t*`Ccp"LU ! PL(22uVf99f9P&#0%#654&#"#4>32#"'6324&#"32>&@C:6'ExTh3!<;$/NJ(%]5! >?08d< %#"&5463234&#"32`]\58xx;,-<<-,`qq\[x%UX@?Z?Pg%-&#"#4654#"632#"&546326324&"2g=<5x98:GjhjTRV1*HB6LjLLj4LP ! UL"mfcjlfGVu00(f99f9<%0%#"&5473326=#"&54632537/5&#"32#dpB7<83N=VTDA;xaaaax4.%-1\q~n(%&8JI>(E54#"732XkCjpA,A Bx*4Ml8>\t#3* $"n6O7?.%R}9GxU1_ "9&%Ak+(7 8*:X);;!:# Yh .&@*>Nx26M ^$<%D#"&547332654&#"#523254#"#523254.'&5473%& B$8-HL$c <7&>%# JH % F5GX$x_O?"5 E 2 "E(>"oa6:"&P.,P/-& / %#"&547332654.5473vXSrn2%)7):9)n+3+U\\Q%..(#1 #@-!2#">$%#"&54675#"&=3;4&#"326f`jZi$n~ n<:;;B46@dkq^Zw$H&;GF<4?>(4%#"&54>=4&#"#&546324&'326)(df(&#KS71'9T R>8>B08%#"&547&#"#.#"53&546326324#"32b\Xv 0-'"60 3=S2+ &;8M/"8DWqhYJ/9[TdnZVVZ_oX1#/L2;Y5# )D-- dYZz::pf||t #"&54733254#"#&54632c_Xun0+XQ)( n qW>_g\U704/*1e,c=+ Va2%4+532654&#"#&54632#"&547332>OM#%5"*3n|QLw0*4%eg\wn01' HZ7%!&*)NWMI.GS@i{g[0&$*2>6%Q$0%#"&547#"&54632632$4&#"324&#"326Qe`]qK1;:EnICbD2JMR  !")1/.0./*br]X"Z;HYNAE,s>*0!%4FL2/CE"%#654#"#"&546326324#"327n7EHd_\qj^U@5LR[X..,1Wokiditaup\_y;;cluE04G%#654&"#&546320n08f81n1ud^{d[gX4CC4`_`Mds)%4+52654.#52#"&547332>5OM6+ "87&ZYch_una *HZ%7 Z 7%b%"kye]0&$*p+)`F&'&%53&54632632632#654&#"#54&#"#4654#"Ee_WA00QI42JGZ%n%#'Cd!!DdG-KE23?\I5WZMZ}.::.bHAQF=)6i $\",k +) b&fzw_[t l61h{aZ5KJ62??&4327#"&#"632#"&54&#"326*7!)q.&8@[w}derJ=537>21;' n .0 t[cs{f6JK52??&%#654&#"#54.#"#&546326323m2&)%d%)&2m3^UX;;XU^F|Q+>)&((&)>+Q|FVq;;q.%#"&547332654퀆.5473}_V n 412>:G#^.BB.n-AA-8118\a]T-"2=,0D(c< +5&" ( =,2D K,#"&5473325"&547332654'53273g_Rwn,,[(:(b|SZ!$((z_gWNA24%.7ez 186A=!!/ L=9"#54.#"#&54>53632632#654&'2d !)&2m37WjkW7t&!>$#0H&%G+:\R^3m2&J7-@+$>+Q|FSO<'#-4' ,>;;sTF|Q+>%#"&54'332654.5473Rizuq-: n**P`jg(3,,(2HY-7B%#"&5473327654'#"&547&546324&"264&#"26+BXQ*2n2pi)(XP@>VDCS>AQ2lx+B*'H()#!+*D*VO}K1xs{lYVY"!AYQ=Q0+H>YVB<)!,,!%33#,.!"118#"&547332654#"#54#"#"&547332>32632uhf4?!BO/5d %&E)/<"n"!)1+ (S9Gsvcb)*'5".MB)54#"#"&54632632$4&"32m1UQ/f FK !& >$"iXTtzSTA1XFVaa1R12():hb&J4& '`5C("c"V):XglSRkFF`Gw')R;;R;!#"&54733254&#"#&546323Rvn3.R<$) n WHD3n9VOA24%/6e"#/">GT0w6%#654&#"#54&#"#.#"53&54632632l!%$c,S8ETOU+ $73I-#=MWr^^K55[N]?HF6)>'%+82=~Ic3$)N- dNb;9g$#"&547332=#"&5473326=3&'~Qyn4/V4COY-n-3- 1n>F?AXO?24%/6e%aPLGII-4( (4%#"&5463254.#"#4654#"56326324&#"326ubaw[@8 4j3-4/8A&+RF=n734>;12>auu`[u Z! O6l$;;`J5KK52??#%#654&#"#46326324&#"67n731+9nv[Fc8SRY#33Zpca2A>+Y^@BZ/r,E4r2%#"&5467332654&+53254.5473r9)_VwB83nk('~Ec78-RM8B8n&43#'9~Da([KLPR=A9"^/+9( 3 %"&5463234&#"326uv\@8nn915=;15;auta[u K2DJ62?F&2&#"#4654#"632#"&546326324&#"3264-3j48@[wabu=FR+&A8/=537>21;T6O !Z,$ u[`uuadJ`;;$ 6JK52?? %1%'#"&547332=#"&463237'.#"326 _g`Sun4-RC%D]XCM6n___B#-0!@`fUPA21(.7e )YXGqQ bo%&")%#"&53734&#"326ց`avnwr_Vjn=537=31;_vv`dt`6JH43B?M#"/#"'532654&"57'&#"&546327654+53232654.'7M9Z)Zo+0]@RHN&2 N[)b?S'G7;.K P H9NSE A->Am!Xs $3:9=MOZ Z&39z 2 " 0'29%#"&547332654+52324#"#53254.'.'53*GL*`{ p t0BO9% NP #R-;[$;n7IH2;??D-A"g^#s&-?ZTZ  3"$ 1-<H="DK4.5432# 57>3232654.54323254.43232>54#"P1;1 #9L*16 4kJNNjSmuT:5?5% Q#3<3=m(5<%8%! !O4#!A=!&%22/$d!e~q'.!/:%5D8*K('%23"'#527&54>2'4"6A+!pSUnDO#9;<::#nGGbc++c+\,B!!A-`YBB:&&#%2#"&54>73254#"'7#5!2(< riZn!3 5-dZ2wk%=?!ioaP#0/-2{p0c2<#"54654&#"#".54732654'53254/.54632$ "#Rz>WPf%C?%K-l6C7lK]PEXT "("""* F/H/Jl52lVi!A-^3% +R4-nJb!$JbKOYF'0:4>323#3##".54>;5#".7;54#""32=(O7````7O()S;GG!81E$!2}TkhU8&!-0 4 c! 1"2654&632#"&'732654&#"7.546323#54BBhBAB$(B>!90F$!3Zpab3/_KJ^\JM_:9&!-0 4 d qzO'7GM6'Y'6!'YGOp#q"(C  'COp#q"(v 'v^O6p'Y(6 %'YZHORp'o(R %'oTHO+p'k !&ko5JJ#l)9#l=I*p#q*"&#qJD#l+C!#l:KDR#l+CR#lKD'j+C'jK$8$!#632#"&'732654&#"7#3!3# #)C? 91E#"+] K+&!-0 4 V'C7&z|KDH'k+CH'kKR<'o,R<'oL0#j",v;'vJ'v.;$'vNJR#l.;R$#lyNJf#q.;`$#qNPRC#l/CR#lOPRCp#qRp#qPcC#q/m!'qOP6C'Y/6%'YOB'v0<8'vPB#l 0<8#l"PBR#l0!'v,SLy#l3:&>#lSP#l5?r"l6UPR#l5?Rr%#lUPRp#q?Rr"q1Pm'q5mr%'qU y#l6"lp V Ry#l6R%#lgV y'#ls@#lF y #l9"#lu# Ry#lR#lV#l7-g#lWRV#l7R-#l WmV'q7mA'qW6V'Y76E'YWL3#j8:3#jXLR'o8:R'oXL8'Y8:8'YvXL &8'v+o:'v+L'j#\u8:'j-'o9&opYR#l9R#lgY 'C0:'CZ 'v':!'v,Z 'j3:'jZ #l1:#lZ J#l4:R#lZ#l;"lt['j;&jp [#l< %"lr\B#Y="YR]RB#l=R#lV]mB'q=m'q^]Cm'qK-R&jkW'mZ %&ms\'#Q)D4#l=AJ#l$J %#l_D"$N "D#v#Y$` #v#+'C#Y$  &C+# "$YR""&$'o,Y 'ooJ#YJ "Yh&$#k] #]t&$#k^ #^j*#c5"$k "#&$#ko6 #okJ#kJ "khOJp#l(3 %#lfHOp"(: "HOp'o( "ohHO#vi #v,+p'C  &C+O}#"(YJ"Op#Y"(o1 'oOJp#Y3 "Yi?",oC"j?J#l,CJ#lL(3#l2#39%#lR("2c#9"R(#v#!#vD, 'C9 &C+(#H"2Y#a"(!#Y"2o4#9'o(3#Y(#39#Y)(#]b#!"]n,c(#^ b#'Cc("bg#"c(#ob# #oc(3#lb#3I#lcL3#l8:3#lHXL"8J:"XL#]q: "]_+rL'Cq:'CrL"qB:"r L#oq:""oJ5rL3#lq:3#llr'C< %!&C,\J#l< %#l+\"<2 %"\'o< %&oq\P"~.1P#>.1P ".1P" .1P ".1P"!.1P"U1P#"1"">R"B"  "h"!sn"&q"":"l.5">n.5 ".5" .5 ".5"!.5"">"p" b"^"!d8""v178"">178 "2.78" .78"5+78"!%78"O78#"7"">"t" k"\"!rKq"Ln""".9">.9T ".9:K" .9<N ".9?J"!.9;F"97T""9E9">">"}" l"e"!im"l"""~.?#>.? ".?" .? ".?"!.?" "> ]" 2"  -" "! "m.E">p.E ".E" .E ".E"!.E"DE""rEv">%$" %5G"!%Xp""%+#:I%#>4I"[%I" h.I,#:I""!~1I#I#"I")9">)P")" )$")"!)]j")j"")P"2^1P"=;1"2L5"=(58"2J78 "=W7"29"=9"2^?"=;?"2ME"=*E#2'I#=I9P"DV9P"DW9P "DX9P"DY9P "DZ9P"D[9P"D\9P"D]~#^{#_#"`#a#b#cn#dq#e8""r8""s8 "t8"u8"v8"w8"x8"y#z#{#|#}#~#9q#EBn#N8+#8%#8#8#8,#8"#8#8## ###o#{#Cj#Ogj#sP"km1P"qo19P"D9P"D19P"D,P#19P"D#kp#q"2"=|#T3:732>73#".=3   !$[~'"  C?bT3 3565#V2RLJ6Q3"=r>0B~#>"k\E"q^E"0]E"1]E5# A5 ">uA#E"]E#k%p#q%"2%#=j%">"XM  3#'#5!#5RrF73ʇ,*PPm+>.EB 356=#B,*PPm+>.EBy} 7356=#B,*PP}m+>.EB #0.=PP*,}E.>+mG #54>73#54>73·,*PP,*PPm+>.E}m+>.EI 356=#7356=#I,*PP,*PPm+>.E}m+>.EHy} 7356=#7356=#H,*PP,*PP}m+>.E}m+>.EI #0.=##.=PP*,aPP*,}E.>+m}E.>+m>  ###5353 t3t>#3##5#535#5353tttt2, 2"&462IIhIJJ34IIhI-E "&=43E  Y} @\>&h\ 7#5!#5!#5㖒@$y  $/:E2#"&46"2654&%3#2#"&46"2654&%2#"&46"2654&?WX=>XX=)):*)2C[B?WX=>XX=)*:))S?WX=>XX=)*:))W?;WWzWP))))O 'W?XX=)*:))?WX=>XX=)):*)2C[B?WX=>XX=)*:))S?WX=>XX=)*:))W?32#547'57>54&(:NA"r?M  |&%OAb7" c;Q6!/$W:" |Yt+1,!5,hh"%q7#"&/5326F+8]_8+JN"y7&&7y"="2q57>32'&#"M+8]_8+JNU#z7&&7z#<-3#'#6xyllx-챱D' ' J I5[A!5[ASQ3#WYW!`-,;#"&=47.=46;#"  (G>H:!H>G(  !P sSIk1M9ISs  ?,m1-+7'57>=4'.'&+532+5327>76=4&!  (G>H!:H>G(  m,? sSI9M1kISs  >-@#"c"@i#c"py#"M0 #67!5{e0nk}A####5!2t\b?b+/Xw`6PPH19j19%!53#5!2#3+99$Jk32l<<1r$rKtEDtLe" IqR %#.=3#VV1? b8$82#v"q/'~e' I A?'yZ'y 83#"/&#"#7>323267^TG4M#!&^ ZA;6$+#?: ! !9= " q57>32'&#"M+8]_8+JN#z7&&7z#< Z&5463276327632#"/#"&/#"&54?#"&546?#"&54632'.5463281!"0J%.!/e} "33" |h/!'I1!"0J'!/e} "33"zh1!%I^"33"|i4-#&I1!"0J'!.e} "33!|i-#'I1!"0J'$,f%1&y.'y y=4A'qe'q'qq%'y'yL'y~y~%'yt'y'y'y?y?% 'yy@%8'y<&y<'yy9) 'yc'yc&y& 3y%'yB'yy%'y'yA'yyM4632#".74&#"326EX/@! .?"!"# #$ix#BL2"@L2\ADXZ?C}#7#5ZZZ`DqKKN ##5#53#5N._m_jK]]J J#632#"&'332654&#"#78'(@O\IBSY<"('#+Q)KYRAEVE85,%&, L'4>32#&#"632#"&7"32654&55"/"S' "&5;IUCVK'&(%Fb./"&&-)%M=EWpt,#"+-"#*Q #67>7#5QQL[,AB\d|T^K P)#"&547&54632"32654"2654&A;JZGHZJ;S@?St<&'&E '&B'&[8&G=LL=G&:2BB &! %%"&L _eK ##5#5353KuMuuMGmmGmmK!!7GK`&G_ 3#.546@3''3@811[zABy\NFE_gKe3632#54#"#Z&B8=Z:!(Z`/4;5:#*M(8{8Ht+Iu8N)J*L8Q *P2LKV(LoVLe_#PKA#PI(73##5#535#535!!3#OOOOIPP?SkS BH3#3#63232>7#"&#"'67#53.'#53.54632#4.#"誉 $i C$>&0 R)DZo{n> o9W5" "5? N 3N ZUM+N@N&-e3JO0  8/")<8%3#4&'#5#54&#"#3>3267sG?F'"cs)#-3#C.j) &i UEh %Qln &82C)#P*.###'##5#535#53533533##5##3'#3----33]5e415]]WggWWWWP&5VPX%>73+!32#54#!#>;,>L7 APk0s5:q7W0 zYI/X+#%%!5##5#"&546325#5353"32654&%6u3Jj||gF2NN}3=>30:;AYYS+{%?S))VVUGLT$\.".'#73&47#73!2.#"!#3#32>7BOB7 M,C0> 7D+-.BZU<"9N %:eAD* D|WHD" DFM"8E +654#"4''#67.54632#526h SKt7G #iUE^WAFu9693 _wk2|$?:N!ViXCh_cGcs"2 '53#&4.'>73#5.546 Dnj  $ ksDts7&&76z%!$* }hi jmpr!>7MW@/IdFAYM/Y!&'7!N.AXLd_/MYA,w3K:c!3P , !.'>7#=,0I>>I0,=#B(M+9L<7n,4.E99:;B.7)R,4.H879;C.4,.J(R;33?N'M,0I'U;02>P(H0< #iS0OWM#!5MNPN9'yB'yy-#5!#5{NMdpp8 , !&'67*6n,ii,n6>F8-||-8PR###P:OG:ORHHGI@0 G{237''7'7/DE-''(&,EDH,9;;9,E +8<\2#&547#"&54>?>54#"#>7326%3#2#.#"32673#".54676R.@ 3{DB5B!='.#:') ifi 5'*:)7UUHWk)@ H ,kpN1G$-$?  =:. ,%  # NG &(5!M9 )'D?>54#"#>73264632#654&#"#".547332654&/.UU.@ 2{DA6B!='.#:') ifi 5'*:)78t\KVl&!'Maxf2J$nR$/&?52!  =:. ,%  # NG &(5F]>6    FL\&' 6 0#&8rN#0>2#.#"32673#".546763#%4>32#"&72>54&#"OHWk)@ H ,kpN1G$-$?!UU|@zOR_wP^%9 +%6O,M9 )'D723254&54673#"&#"#4>232>73#".#"#4>& *"-'aK3 y"4.=Y%( qb:)E'79'y,MQpfHK'=3Ma`4 4M&y#)#  Rf#)#Q]3NQ4!M )"32654'2#"'####53533533#6hBA54BWaac2uCCu2]JM^^JKԡ}zN7aaE3333EO;'Z2/R5+F133@  ) #"&54$ 32654#32#'32654&+<뢚 dLPYEwr"#r鞠윞PB@P]&O3273#".5>5444632#".5332654&#"#'.'$8 j0&9&   =BRq5kRS_+T?-4#o 9$ % }Q!=2# !%>LtsP z52FX^N;( %D:!.)6#&#"#"&'332654&/.54632##33#X[%+%2H@:dYWc\40,1'/@F<\R-2%[^U[VT[RH  <6FOOI%'! 96CJ7]^8],g/M$.g($.F7!33@G##5!##33#/_m?^U[VT[zNN]^8]) ),;9; J. %H"")l=XMbX/X?6X(D4@&f)33@g;9$.G$$.C*  /c 3!5!} ]'}<  1 >B##3#/4632#654#"#".547332654&/.3#P1\h:[\iXGPTO)/+A.0 -J/.C!X-)/8);6/oUUkXKE\61 2" ."+0$%&  $ +!"G)G(V#u "Y{V#u "tY(H#"Y{H"t"YH"u"YH""Y(@#"Y{@""Y(X#"Y{X"u"YX""YX""Y("{Y?,?#,,?#,,#,,?#9,9p#,9#,#,9#,#,#,9?#;,;p#,;#,#,;PC/,&M'B0CLC#LLC#L,#LLC.#YLY#L,Y#LB#L,Y'#LX#LB#L,YC-#[L[#L,[#LB#L,[CO" %F!G<8%P'F !!%5+,qhUi@-4@*F 4@-4@'F3'557#%5+)q.,qhihi@-8@:,4-AҾ-A-A&,4ҾҾ-AҾ&?47Ҿ-AҾҾ9@41-A-AҾ-A' !73#'7#%5+JsٌJu,qh*U*i' %57!'7#5373'5+JtыJt,Fh*U*i'F !!'%5%+/,qhUiUUUB,@@'F @B+@@'F3%%#%5+A,qhwh:9ixi'F D'!53#5!%5+PP=,qhUi-F@' F -F@#35#3#'733!53@hihiX+)q.,qPP'>+44L-8K@'>#4 4'>#4L4-8N@'> N-8 O$ )#"&54632&'.#"#>3 "326543i?js[,@! 9*"'+/=$b:=/0<QQ18tf"K06"0F"F;9JH8;;\ !5!5!5!!5!uu!:}}'}33%!?'}U)= !! !!5 8!TApzy\|]( #!5 #ww3#qWYW!;'7Ka$ -/FUd %#"/73254632'&#"k0?9  S|U#  -9Z6$ sw@\wn9D27#537#5!73#3!'{IDAb#TDDa4wywu8=wywy8- %!5 %5~Q[[.mox- %!=-5~Q[[Jmoy %3!53!V X2p #6Ws 8y'#"&547&'#".5475#"&547&'4/&54>75.'67.54>7'>7$54&/>54.'654lnMQ'}p- # &p|(QM*eK)-%=/E3:%&1% .4$!9/ 3;&)4%'3 '.%!-;)#!()#"fUhrn;0c (?* ;)< ?( c0;ooj6`S,U++R==&) (  7S/'G0*+/*2 !6T1)I1(*  (.SxC(iD3#<('<#"<'Q:Mc)%!5>7#"&54>7#"'=&'(# -6=W'0:797791(W=6- +-3)%99 *!V=(:*'7FuKJuE7'*<(=V! 264632>32.7">54#"#".aF4XX4Ec-9A<:==A8,(2?Q\EA=%X#1!4+Ha9229`C-REFT^NQ`VDAJ:,$la},)^NQV(X++/8/K.'>7'67.K#P+2wY`y),8|7#"&54632&5462632#"'$49'U)Ig^D ii D^gIYC&?B%99$9$#*kKIh!HeeH hIKkP.A'cG%3&32654.'326!5>7#"&54>7#"'<C$/&4 49<=834&J&'(# -6=W'0:797791(W=6- +-3)h" )0,4% 5+-H+,I,+4 %4N99 *!V=(:*'7FuKJuE7'*<(=V! 22.54632>Ec-9A<9==A7-aFm8X`C.QDFT^NS`UB@J)Ham76K .'>7KX'+m`y),ZdLRtC9X+0CO[%!5>7#"&54632&5462632#"''3&.#"32>732654&#"74&#"6$49'U)Ig^D ii D^gIYC&?BC;%Z$48  Z ] <2f6,,5 /'b%99$9$#*kKIh!HeeH hIKkP.A';")4,/; AC;.+8*9: ;F& %#"&46323L4-BB-3"+S%..J.-7#"&46323#54&'.&L4-BB-6 *1P)*w %..J.-7&%{U88 S 7#"&54632#"&46324O91DF/:#O6/EE/9"Q())(&-ƕ&--L-K75%#"&54632#"&46325N:1DF/:#O6/EE/9"?~Ey())(&-ƕ&--L-`#"577>324&#"32>Ihk&'D +V_&B >3*ET30 \TO5= G* 5775DDD Y WpEV;%555755757757775>FF,,,,FF>>>J- 24- 8*1D8\&547.#7>7&'32.'>?/4&54654'>32%267>7632327#"&'&/.'&547#"&'>7&'>;&5477654'4?&54?&5472>54'#"&'#"32?632+%>7.#"327'&5465.'&'6324654.'&#"3263232654&"#"'.'&'&'&'32?6=#"'>7>7&547267327"&+"27>32&#"54#"+654&'>7v60F\ <+/I >0 %$ =. *@i/CCC 2 @('YT w`#4)*DAcE O(X6A($s Q.g-OM 6$" zt  $  tg Q <$ -.S+"-I$& ),8yZX&O#$^&0 %Q?,! . %3 4(  kfd  :S a#*$] aL-=5#,&; C   >    4  B! o+  G+4,<00i 2//@ O 7?C  {&q* $ F &%B./N >d& Y !o+4V Fm#%0#(EE 7@ l}E'! 03 $ n5e5 m"!< "7)`  (@'M ?  ,q& ,, 1"'%llf JA/T8.:S0$Da* @$4fU< >    0)     (v"575"#TXTTXT Le462&0% ,,` 35>5#p*F&@<;38 *$m#"&'#"&54>3267n ~ s /-(0*)/ 6>]U(+4*%2 pG};#327#".'.546;53#"$~ 7# B*6 .0L&C?L#*. %B8|-#"&'4326753t< e4S1 ]ޑg=S  : >726=35!5!####".'7 V|pf X50"q!pqj$PhG&".'#"5462353'32=/;! .EBNI)(655/$76753#"(8P;. T9)UN%>+( Y.54&#"#"54323273Lti#%)D. :>L @QCUw>,&,0' g;1(K4=&}53#5654&""&546T>JD>4CDA  =FVhSSU/?QH3-+  HS=8Ms47332>32#".s 3+%  ?1537+- 0E, ' " 7M-X #"&'33$+.5+ 2.hX#"&'#"&546326735!*T t 1,/)7, /a iwOT'-="#C A5+DD-L#"&'43267535!s< e4R0 `if>R  : ?[DDWX32>32#"&54735!3" "4.;T .ij8]M012I]m/*pDDX#"&'&'35!0 !v5'5Ui")K;,>%DDh5 $#"&'#"&54632675!5!s  &5 s 4%0(03"-LL ' H)4> "5566-4#"&'43267535!5!s: e4M!0 `xxhc-S  + ?n11u003"&54632#"54654&#"e 54&#".#"'5327#"'5327462#"4654&"#"&2".5432#".=#53>32;+tha]]f]c]k`ZiH+)2F +5 30603 -b^u`$AJ?:)LM G((r)'hWaE)OO:832#".54632#".=#53>32;!!#Dia\]c_d]m]Z_f\e\ 1N2hG+)2 F +5  ,'13 )e]u_4[eG/KL C  ((l('E''m)(N%6= E)OGB8=p-6-+#&;! C0!+euBH^SfCm~ppf -R32654&#"'326767!432327632".# #53>3#.#"!3O-;2]4#nMc,5 np,;` 30613mK8^w]UX/DvK`'7#C@Y a\"}4@*,1Bk)2=M*/7f*,"/C C+j#E^Shp@]F2&Ysp'h)&p'_''I'i''o'h''l'a'''']''z'J''w'''j'V J62#"&5474>54'#"5467654+532>7675;!##+##"$-"%+%#eM .-R2/F\-A -2U,0,7Bap aqY6(/Vr" -"':*:GC@h_;E*H(pP"0,,62I^!pI.44A8 GL$2#"&54.#";!#&#"32>32#".'67&'+##53>32375$-"$>?'c|;^> $I"9 'J/321$-"-kl'J/?"m^ygloW%;^> $I"9 4" -"xu{ -4%(P5^G5spbY0Lovp&#C#'&~'&~'&n'&\''R&VfAK%4>54'#"5467654+532>7675;+#+##""&4632L%+%#eM .-R2/F\-A -2U,0,7B^00Tp^qY6(/V"00"$..z:*:GC@h_;E*H(pP"0,,62I^!pI.44A80D00D0GL!;#327#".'.546;53#"5!$~4$ B-8' .0L&Fjx3267n ~ s /-(0*)/ 6>]U(+4*%2 pK######535432&#"35432&#"KSLL)'&)'&]LL]Ai*5Ai*5#5####535432&#"LL)'&}}L]Ai*5####535432&#"35LL)'&'L]Ai*5 )#5######535432&#"35432&#" LL)'&)'&}}LL]Ai*5Ai*5 '53######535432&#"35432&#"~LL)'&)'&'LL]Ai*5Ai*5A?!#5#"&=3326=#"5473;./&546;"!32653#5#"&5#$:hV_Z3>Їi*.'&(0)Z3>:hV_@WbYa:0 '&-] * a:0R@WbY A6!#5#"&5332654&/&5463!!!326=3#5#"&5$:hV_Z3> .'&WZ3>:hV_@WbYxa:04 -]a:0:@WbY &A84!#5#"&5332654&/&5463!632#4#"#$:hV_Z3> .'&:d 97 V/=@WbYxa:04 -W'N5Jd<.D &A8.6"&5463!!32653#5#"&5#3##"&=3326=#"3;LQ6\Z3>:hV_N:hV_Z3>*O@CSa:0R@WbY]WbYгa:0hU'A8g8!#5#"&5332654&/&5463!332653#5#"&5##"$:hV_Z3> .'&Z3>:hV_N@WbYxa:04 -a:0R@WbYyD &IX'52-273#"&'30d;#@Qd55OJIX&VCX%+533>53CY{nm@]s;1q=X##5>7'3>=3g"3! .o;2AQ(G3 63"-8M5?0X!5!##RSssO^X.'!5!#!3  ) 4|m(/ sP&5%]LX.'!5!#!5!>7#L3Y-?+A#m * sP'5$+s * >M#54>76=!3!M@[[?)=E7D|2H)'=*ek'H0, /B P_X 2!.'!!T>c8% .#X'7D3X"2 ? X.'!5!#  )Cɠ4m(/ sP&5%>X%.'!#'532=#5!# &+**UA WB( 10&n&" #?B,tvs#)9"2!5353w#wwL&L&L&&L'&=rX&;=X&,=X&&MbX&/X&?6X&OdX&1 X'3sX'LaX&5)X''@F>8X&LX&>S&HCyX&CIgX'HLaX&/H8NY&MEOUX&ZF=^X'tP8WX&)?X&LX&>X'&Mb&0L&OU&8S#53>753m_l B9*"# 354>54&#"34632#3 #*#8#*#H=5#.$,888hg0 2#,8"7A/2'A{hgV?*@  +w F1$,A ~   T k 6   V  Hx X$n$ $H $TTCopyleft 2002, 2003, 2005, 2008, 2009 Free Software Foundation.Copyleft 2002, 2003, 2005, 2008, 2009 Free Software Foundation.FreeSansFreeSansBoldBoldFontForge 2.0 : Free Sans Bold : 29-4-2009FontForge 2.0 : Free Sans Bold : 29-4-2009Free Sans BoldFree Sans BoldVersion $Revision: 1.146 $ Version $Revision: 1.146 $ FreeSansBoldFreeSansBoldGNUGNUhttps://savannah.gnu.org/projects/freefont/https://savannah.gnu.org/projects/freefont/The use of this font is granted subject to GNU General Public License.The use of this font is granted subject to GNU General Public License.http://www.gnu.org/copyleft/gpl.htmlhttp://www.gnu.org/copyleft/gpl.htmlThe quick brown fox jumps over the lazy dog.The quick brown fox jumps over the lazy dog.polkrepkoDovoljena je uporaba v skladu z licenco GNU General Public License.http://www.gnu.org/copyleft/gpl.html`erif bo za vajo spet kuhal doma e ~gance.CE  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ softhyphenmicroAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflexTcedillatcedillaTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019auni019Buni019Cuni019Duni019EObarOhornohornuni01A2uni01a3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01aauni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01b2uni01B3uni01B4uni01B5uni01B6Yoghuni01B8uni01b9uni01bauni01bbuni01beuni01bfuni01c0uni01c1uni01c2uni01c3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F1uni01F2uni01F3uni01F4uni01F5Wynnuni01F8uni01F9 Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021cuni021duni021Euni021Funi0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233dotlessjuni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028funi0290uni0291yoghuni0293uni0294uni0295uni0296uni0298uni0299uni029Auni029buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02ACuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02b9uni02bauni02bb apostropheuni02bd ringhalfright ringhalfleftuni02c0uni02c1uni02C2uni02c3uni02C4uni02C5uni02c8macronmodifieruni02CA gravemodifierverticallinelowmoduni02cduni02ceuni02cfcolontriangularmodcolontriangularhalfmodringhalfrightcenteredringhalfleftcentered tackupmid tackdownmidplusmodminusmoduni02DEuni02DFuni02E0uni02E1uni02E2uni02E3uni02E4 toneextrahightonehightonemidtonelow toneextralowuni02EAuni02EBuni02ecuni02EDuni02eeuni02EFuni02F0uni02F1uni02F2uni02f3uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02fduni02FEuni02FF gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307 diaeresiscomb hookabovecombuni030Auni030Buni030Cverticallineabovecmbdblverticallineabovecmb gravedblnospuni0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031bringlefthalfsubnospuni031Duni031Euni031Funi0320uni0321uni0322uni0323uni0324 ringbelowcmb commasubnospuni0327uni0328linevertsubnospuni032Auni032Buni032cuni032Duni032euni032funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337 slashlongnospringrighthalfsubnospuni033Auni033Bseagullbelowcmbuni033Duni033Euni033Funi0340uni0341uni0342uni0343uni0344uni0345uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362uni0363uni0364uni0365uni0366uni0367uni0368uni0369uni036Auni036Buni036Cuni036Duni036Euni036Funi0374uni0375 ypogegrammeniuni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosthetasymbolgreek Upsilonhookuni03D3uni03D4phi1omega1uni03D7uni03F0uni03F1uni03f4uni03F5Iecyrillic_grave Iocyrillic Djecyrillic Gjecyrillic Ecyrillic Dzecyrillic Icyrillic Yicyrillic Jecyrillic Ljecyrillic Njecyrillic Tshecyrillic KjecyrillicIicyrillic_graveUshortcyrillic Dzhecyrillic Acyrillic Becyrillic Vecyrillic Gecyrillic Decyrillic Iecyrillic Zhecyrillic Zecyrillic IicyrillicIishortcyrillic Kacyrillic Elcyrillic Emcyrillic Encyrillic Ocyrillic Pecyrillic Ercyrillic Escyrillic Tecyrillic Ucyrillic Efcyrillic Khacyrillic Tsecyrillic Checyrillic Shacyrillic ShchacyrillicHardsigncyrillic YericyrillicSoftsigncyrillicEreversedcyrillic IUcyrillic IAcyrillic acyrillic becyrillic vecyrillic gecyrillic decyrillic iecyrillic zhecyrillic zecyrillic iicyrilliciishortcyrillic kacyrillic elcyrillic emcyrillic encyrillic ocyrillic pecyrillic ercyrillic escyrillic tecyrillic ucyrillic efcyrillic khacyrillic tsecyrillic checyrillic shacyrillic shchacyrillichardsigncyrillic yericyrillicsoftsigncyrillicereversedcyrillic iucyrillic iacyrilliciecyrillic_grave iocyrillic djecyrillic gjecyrillic ecyrillic dzecyrillic icyrillic yicyrillic jecyrillic ljecyrillic njecyrillic tshecyrillic kjecyrilliciicyrillic_graveushortcyrillic dzhecyrillicuni0470uni0471 afii10147 afii10195thousandcyrillictitlocyrilliccmbpalatalizationcyrilliccmbuni0485uni0486uni0487uni0488uni0489uni048auni048buni048Cuni048Duni048Euni048F afii10050 afii10098uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7Haabkhasiancyrillichaabkhasiancyrillicuni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04cfuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8 afii10846uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFDzeabkhasiancyrillicuni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04f6uni04f7uni04F8uni04F9uni0510uni0511uni0512uni0513uni051auni051buni051cuni051duni051euni051funi0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564 echarmenianuni0566uni0567uni0568uni0569uni056A iniarmenianuni056C xeharmenianuni056Euni056Funi0570uni0571uni0572uni0573 menarmenianuni0575 nowarmenianuni0577uni0578uni0579uni057Auni057Buni057Cuni057D vewarmenianuni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058A afii57799 afii57801 afii57800 afii57802 hiriqhebrew afii57794 afii57795 patahhebrew qamatshebrew holamhebrew afii57796 dageshhebrew siluqhebrew maqafhebrew rafehebrew afii57842 shindothebrew sindothebrewsofpasuqhebrewupperdothebrew alefhebrew bethebrew gimelhebrew dalethebrewhehebrew vavhebrew zayinhebrew hethebrew tethebrew yodhebrewfinalkafhebrew kafhebrew lamedhebrewfinalmemhebrew memhebrewfinalnunhebrew nunhebrew samekhhebrew ayinhebrew finalpehebrewpehebrewfinaltsadihebrew tsadihebrew qofhebrew reshhebrew shinhebrew tavhebrew vavvavhebrew vavyodhebrew yodyodhebrew gereshhebrewgershayimhebrewuni0A01uni0A02uni0A03uni0A05uni0A06uni0A07uni0A08uni0A09uni0A0Auni0A0Funi0A10uni0A13uni0A14uni0A15uni0A16uni0A17uni0A18uni0A19uni0A1Auni0A1Buni0A1Cuni0A1Duni0A1Euni0A1Funi0A20uni0A21uni0A22uni0A23uni0A24uni0A25uni0A26uni0A27uni0A28uni0A2Auni0A2Buni0A2Cuni0A2Duni0A2Euni0A2Funi0A30uni0A32uni0A33uni0A35uni0A36uni0A38uni0A39uni0A3Cuni0A3Euni0A3Funi0A40uni0A41uni0A42uni0A47uni0A48uni0A4Buni0A4Cuni0A4Duni0A59uni0A5Auni0A5Buni0A5Cuni0A5Euni0A66uni0A67uni0A68uni0A69uni0A6Auni0A6Buni0A6Cuni0A6Duni0A6Euni0A6Funi0A70uni0A71uni0A72uni0A73uni0A74uni10A0uni10A1uni10A2uni10A3uni10A4uni10A5uni10A6uni10A7uni10A8uni10A9uni10AAuni10ABuni10ACuni10ADuni10AEuni10AFuni10B0uni10B1uni10B2uni10B3uni10B4uni10B5uni10B6uni10B7uni10B8uni10B9uni10BAuni10BBuni10BCuni10BDuni10BEuni10BFuni10C0uni10D0uni10D1uni10d2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10f9uni10fbuni1E00uni1E01 Bdotaccent bdotaccentuni1E04uni1E05uni1E06uni1E07uni1E08uni1E09 Ddotaccent ddotaccentuni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1D Fdotaccent fdotaccentuni1E20uni1E21 Hdotaccent hdotaccentuni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3F Mdotaccent mdotaccentuni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55 Pdotaccent pdotaccentuni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5F Sdotaccent sdotaccent Sdotbelow sdotbelowuni1E64uni1E65uni1E66uni1E67uni1E68uni1E69 Tdotaccent tdotaccent Tdotbelow tdotbelowuni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1e9auni1E9Buni1EA0uni1EA1uni1ea2uni1ea3uni1EA4uni1EA5uni1EA6uni1EA7uni1ea8uni1ea9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1eb2uni1eb3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1ebauni1ebbEtildeuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1ec2uni1ec3uni1EC4uni1EC5uni1EC6uni1EC7uni1ec8uni1ec9uni1ECAuni1ECBuni1ECCuni1ECDuni1eceuni1ecfuni1ED0uni1ED1uni1ED2uni1ED3uni1ed4uni1ed5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1edeuni1edfuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1ee6uni1ee7uni1EE8uni1EE9uni1EEAuni1EEBuni1eecuni1eeduni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1ef6uni1ef7uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBClenisprosgegrammenipsili perispomenidialytikaperispomeniuni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCC psilivaria psilioxiapsiliperispomeniuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDB dasiavaria dasiaoxiadasiaperispomeniuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECdialytikavaria dialytikaoxiavariauni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCoxiadasiauni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200auni200buni200cuni200duni200euni200funi2010uni2011 figuredash afii00208uni2016uni2017uni201buni201funi2023uni2024uni2025uni2027uni2028uni2029uni202auni202buni202cuni202duni202euni202funi2031uni2032uni2033uni2034uni2035uni2036uni2037uni2038uni203b exclamdbluni203duni203euni203funi2040uni2041uni2042uni2043uni2045uni2046uni2047uni2048uni2049uni204auni204buni204cuni204duni204euni204funi2050uni2051uni2052uni2053uni2054uni2055uni2056uni2057uni2058uni2059uni205auni205buni205cuni205duni205euni205funi2060uni2061uni2062uni2063uni2064uni2070uni2071 foursuperioruni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080 oneinferior twoinferior threeinferior fourinferioruni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Eliramilluni20A6uni20A8 afii57636dongEurouni20B0uni20B5leftharpoonaccentrightharpoonaccentuni20D2uni20D3uni20D6uni20D7uni20DBuni20DCuni20DDuni20DEuni20DFuni20e0uni20E1uni20E5uni20E6uni20E7uni20E8uni20E9uni20EAuni20EBuni20ECuni20EDuni20EEuni20EFuni20F0uni2100uni2101uni2103uni2105uni2106uni2107uni2109Ifrakturuni2114uni2116uni2117Rfrakturuni2120uni2121uni2126uni2127uni2129uni212auni212buni212euni2132uni2135uni2136uni2137uni2138uni213auni213buni2141uni2142uni2143uni2144uni214buni214duni214eonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215Funi2160uni2161uni2162uni2163uni2164uni2165uni2166uni2167uni2168uni2169uni216Auni216Buni216Cuni216Duni216Euni216Funi2170uni2171uni2172uni2173uni2174uni2175uni2176uni2177uni2178uni2179uni217Auni217Buni217Cuni217Duni217Euni217F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CA universal existential Delta.mathgradientuni2215uni2423uni262Cspade heartopen diamondopenclubspadesuitwhiteheartdiamond clubsuitwhiteuni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2740uni2e17cresc_cyrillic dot_cyrillicuni0A30_uni0A4D.blwfuni0A35_uni0A4D.blwfuni0A39_uni0A4D.blwfuni0A2F_uni0A4D.pstfglyph152glyph153glyph154glyph155glyph156glyph157uniA30_A4D.blwf_A41.blwsuniA39_A4D.blwf_A41.blwsnounicode_3_1_3glyph161uni0A30_A4D.blwf_A42.blwsuniA39_A4D.blwf_A42.blwsnounicode_3_1_2fnounicode_3_1_30uni0A28_uni0A42_uni0A70.abvsuni0A3E_uni0A02.abvsuni0A09_uni0A71.pstsuni0A0A_uni0A71.pstsuni0A13_uni0A71.pstsuni0A15_uni0A3C.nuktuni0A18_uni0A3C.nuktuni0A19_uni0A3C.nuktuni0A1A_uni0A3C.nuktuni0A1B_uni0A3C.nuktuni0A1D_uni0A3C.nuktuni0A1E_uni0A3C.nuktuni0A1F_uni0A3C.nuktuni0A20_uni0A3C.nuktuni0A22_uni0A3C.nuktuni0A23_uni0A3C.nuktuni0A24_uni0A3C.nuktuni0A25_uni0A3C.nuktuni0A26_uni0A3C.nuktuni0A27_uni0A3C.nuktuni0A28_uni0A3C.nuktuni0A2A_uni0A3C.nuktuni0A2C_uni0A3C.nuktuni0A2D_uni0A3C.nuktuni0A2E_uni0A3C.nuktuni0A2F_uni0A3C.nuktuni0A30_uni0A3C.nuktuni0A35_uni0A3C.nuktglyph194uni0A39_uni0A3C.nuktuni0A05_uni0A3C.nuktuni0A06_uni0A3C.nuktuni0A07_uni0A3C.nuktuni0A08_uni0A3C.nuktuni0A09_uni0A3C.nuktuni0A0A_uni0A3C.nuktuni0A0F_uni0A3C.nuktuni0A10_uni0A3C.nuktuni0A13_uni0A3C.nuktuni0A14_uni0A3C.nuktuni0A06_uni0A02.abvsglyph207uniA35_A4D.blwf_A41.blwsuniA35_A4D.blwf_A42.blwsuniA30_A4D.blwf_A4D.blwsuniA39_A4D.blwf_A4D.blwsuniA35_A4D.blwf_A4D.blwsffffiffl m_n_armenian m_e_armenian m_i_armenian v_n_armenian m_x_armenianuniFB1DuniFB1EyodyodpatahhebrewayinaltonehebrewuniFB21uniFB22uniFB23uniFB24uniFB25uniFB26uniFB27uniFB28uniFB29shinshindothebrewshinsindothebrewshindageshshindothebrewshindageshsindothebrewalefpatahhebrewalefqamatshebrewalefdageshhebrewbetdageshhebrewgimeldageshhebrewdaletdageshhebrewhedageshhebrewvavdageshhebrewzayindageshhebrewtetdageshhebrewyoddageshhebrewfinalkafdageshhebrewkafdageshhebrewlameddageshhebrewmemdageshhebrewnundageshhebrewsamekhdageshhebrewpefinaldageshhebrewpedageshhebrewtsadidageshhebrewqofdageshhebrewreshdageshhebrewshindageshhebrewtavdageshhebrewvavholamhebrew betrafehebrew kafrafehebrew perafehebrewaleflamedhebrewuniFFFD l:PQRSmnnotuxy~B6 R DFLT8armnDcyrlRgeor\grekfgur2pguruhebrlatn   abvsJblwfPblwsVccmp\dligbfrachhlignligatligaznuktpstfpsts   "*2:BJRZbjr`&t &.6>FNV\     "   "     "6 , ywqq@FLRX^djpv|2 ( uvw(V`jt~ (2<FPZdnx%&xZ &@uvw, "*   DFLT8armnFcyrlRgeor`greklgur2xguruhebrlatnkern kern&mark,mark2mark< *2:BJRH  $J""J J$$%%&'(())**--..//22 3344 55 66 77 9: ;;<< ==        "" $$ && 88 :: ??yybbjj~~    NN s$$&&**--224466779:;;<<==DDFHII JJMM PQ RRSSTTUUVV WW XXYZ[[ \\]]         !! ""## $$%% &&++--//11338899::;;<< ==>> ??@@      NNOO  X$%&'()*-./2345679:;<= "$&8:?ybj~Nb0 GDDEFHHIIJJKKNNPQRSUUVV WW XXYZ[[\\]]    !! ## %% ++--//113399<< >> @@     OO@DDFHIIJJMMPQRRSS TTUU VVWWXX YZ[[\\]]    !!##%%++ -- // 11 33 99<<>>@@22 OO\DEFHIJKNPQRSUVWXYZ[\] !#%+-/139<>@Oy$$%%&'))**.. //22334466779: ;; << == DD EF HH II JJKKNNPQRS UUVVWWXXYZ[[\\]]              !!""##$$%%--//3388 99:: ;; <<== >>?? @@22bbcc ~~  NN OO  mm}}XX[\xxyy$%&')*./234679:;<=DEFHIJKNPQRSUVWXYZ[\]  !"#$%-/389:;<=>?@2bc~NO mm}}WWXXYY[[]]xxyyn$$&&**--224466779:;;<<==DDFHII JJMM PQ RRSS TTUU VV WW XX YZ[[ \\]]           !! ""## $$%% &&++ -- // 33 8899::;;<< ==>> ??@@ 22        NNOO  m}WXY[]xy@8X]]^^ffhhmmqqttxxzz{{||}}~~ ^^jjqqxx{{||~~]^fhmqtxz{|}~" }7`n  &,28>DJPV\blB<%8A8 4,i4 %)1579?EI &,28DDnDXIuT;L(B 5lrx~ &,28>DJPV\bhntzhvzWhGhF?gBdR5rLQ[-D!0.3c!!I8w8 $=DIKL NS"U](ee12&&3<<4  !#')*+,-1,28>DV\bhnt $*06<BZZZK4ZZZZ Z `Z `NZf|Z 0ZZZZZZ ZZZZZ   Z@:Z- @F:   ;x~ &,28>DJPV\bhntziswEfUi@@mWs@8jUXV@&@!& &&@*&M&1&:& &&&2&&&-!&.#` $=D]ee4xx567118339<<:QR"%'(*,9;?@ABC 28>DJPV\bhntz"(.4:@FLRX^djpv|DDnD V3VDVDDY5ZDXIY: DYDUUDX0 PY7UuUU$P/[0X-XM/U P0G3>s =[..0E   2 ,28>DJ\bhn| @G  Q{     $(,28<DHLRX\n >~ &,28>DJPV\bhntzW#WWWWWWWWWnny{ynynySyyeyy y4y9yynyyTyynyHy5ynyOyySyUy=y%B%%%%%%1%%%%%%3%,%;%%%%%%%%%%%%$= D]$"(.~l>Yņ:mmpong-0.9.1/resources/CEGUI/imagesets/0000755000175000017500000000000011354734770016624 5ustar andreandremmpong-0.9.1/resources/CEGUI/imagesets/TaharezLook.tga0000644000175000017500000100005411130511363021523 0ustar andreandre E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5kbSE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5kbSE?5E?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5E?5E?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5E?5E?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5E?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5e<e eeeeeee e7E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeeeeeeeeeeeeeeeeeeeeGeeeeeeeeeIeE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeePeeIeee e eeeGeeIeeE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeejeeIee7e<eePeeeeeeE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeejeeeE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5e9eeHeeeeeHee7E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeWeeeeeeeeeQeE?5kbShkbShhhkbShkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeWeeQe7eeeee9eWeeQeeeE?5hkbSkbShkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeWeeQeeeeeWeeYeeeE?5E?5kbSE?5E?5E?5kbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeYeWeeee{h{h{h{h{h{h{hE?5E?5kbSkbSkbSkbSkbSE?5he eeeeeeeeeee {h{h{h{h{h{h{hE?5E?5kbSkbSkbSkbSkbSE?5heeeeeeeeeeeeE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeeeeeeeeeeee{h{h{h{h{h{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5ee eee e{h{h{h{h{h{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5{h{h{h{h{h{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5e{h{h{h{h{h{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eee{h{h{h{h{h{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeE?5eeeeeE?5E?5eeeE?5E?5ehhhhhhhhhhhhhhhhhhhhhE?5E?5dwxhE?5dwxhkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSedwxhkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSeeehkbSaXK{h{h{h{h{h{haXKkbSkbSkbSkbSkbSaXKaXKkbSkbSkbSkbSeeeeehkbS{h{haXKyo^yo^aXKkbSkbSkbSaXKyo^yo^aXKkbSkbSkbSeeeeeeehkbSaXKyo^yo^aXKkbSkbSkbSaXKyo^yo^aXKkbSkbSehkbSkbSaXKyo^yo^aXKkbSkbSkbSaXKyo^yo^aXKkbSehkbSkbSkbSaXKyo^yo^aXKkbSkbSkbSaXKyo^yo^aXKeeeeeE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5kbSkbSkbSkbSaXKaXKkbSkbSkbSkbSkbSaXK{h{h{h{h{h{haXKkbSeeeeeeeeeeeE?5E?5E?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSeeeeeeeeeeee@e@e@ee@e@e@ME;E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5ME;hE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSeeeeeeeeeeeeeeeeeee@ee@VOBE?5{h{h{h{h{h{h{h{h{h{hE?5VOBhE?5eeeeeeeeeeee@ee@c[KE?5{hVOBVOB{hE?5c[KhE?5eeeeee@ee@qfVE?5{hVOBVOBVOBVOB{hE?5qfVhE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5eeeeee@ee@taE?5{hVOBVOBVOBVOB{hE?5tahE?5E?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5ee@ee@lE?5{hVOBVOB{hE?5lhE?5E?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5ee@ee@vE?5{hVOBVOB{hE?5vhE?5E?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeeee@ee@E?5{hVOBVOBVOBVOB{hE?5hE?5E?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeee@ee@E?5{hVOBVOBVOBVOB{hE?5SK?E?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeee@e@e@ee@e@e@E?5{hVOBVOB{hE?5h^OE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5eeeeeeeE?5{h{h{h{h{h{h{h{h{h{hE?5htah^OE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSaXKkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5hsh^Oh^Osh^OE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5hh^Oh^Oh^OE?5E?5kbSE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5e hh^Oh^OE?5{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5ehE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5eeeeedwxh  # $eeeeeE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5dwxhOH8Blg]lǽTOH74/8eeeeeeeeeeeeeeeeeeeee@ee@eetapppptaṩ72*8e`Wf͹ҾRNFReeeeeeeee@ee@eeelpplkbSkbSkbSkbSkbSkbSɥh{h{h{h{h{h{h{h{hṩXTLX  eeeeeeeeeeeevppvkbSkbSkbSkbSkbSkbSkbSkbSkbS{h{hṩu͹ɾid[jyqbz<6-<eeeeeeeeeeppppwwwwww{p_{p_{q^{p_{q_{q_kbSkbSkbSkbS῰ɥhɥhɥh '!$71%# -$D;/-$. ṩ'%"(ZUMʷİTNETxι61)6eeeeeeeepppp}i}i}i}h|i|i}ipppppppkbSkbSkbSɥhɥh񿰗࿰ɥh 1)OF9vsj\B;1/&[REȺwjṩmg^n|iʴ61)6eeeeeeeeppppwm\wm\wm\wm\wm\wm\wm\wl[kbSkbSɥhɥh翰޿ȿɥh 5- _VIr5,sj\wθ:5,:eeeeeeeeppppkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSɥh8/"lcUtk^90#tt/&0Ӽ˽1,%2eeeeeeeekbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSkbS%>5(ypbh_Q/&sj\/&0 zp`z|tf|{eeɥh *#E8BŶkkVOBVOBVOBVOBE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5kbS PG;}pA8+ypbƸ}tf/&0¸  ¸{hṩ{hRNFRҾ͹e`Wf72*8ppVOBVOBVOBVOBE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5 1,"wobvna7.!tʼ~]TG.%. ~NJCNNJCN~{hṩ{h  XTLXttVOBVOBVOBVOBE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5LF;]UI3*{rdH?24-# WSKXvvWSKX{h{h<6-1¹ʼwQH:D<2  {FB2{rd_VH+$    ;83<{vNJCN  NJCNv{;83<ṩṩ:5,:θE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5ldXF?4&&  WSKX~¸¸Ó~WSKXṩṩ1,%2˽Ӽᖍ  {|tf|zp`z$ $0+$0E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^OE?5{h{hE?5E?5E?5ME;ME;ME;ME;ME;ME;ME;ME;ME;h^Oh^OE?5{h{hE?5E?5E?5VOBE?5ɰVOBɰE?5VOBF=0VOBF=0VOBVOBɰE?5h^OE?5{h{hE?5E?5E?5c[KE?5§Ѻc[KѺ§E?5c[KF=0c[KF=0c[Kc[KѺ§E?5h^OE?5{h{hE?5E?5E?5qfVE?5ѺѺqfVѺѺE?5qfVF=0qfVF=0qfVqfVѺѺE?5h^OE?5{h{h{h{h{h{hE?5E?5E?5taE?5ѺѺtaѺѺE?5taF=0taF=0tataѺѺE?5h^OlE?5տѺѺѺlѺѺѺտE?5lF=0lF=0llE?5ѺѺѺտE?5h^OvE?5ѺѺѺѺvѺѺѺѺE?5vF=0vF=0vE?5vE?5E?5ѺѺѺѺE?5h^OE?5{h{h{h{h{h{hE?5E?5E?5E?5ѺѺѺѺѺѺE?5F=0F=0E?5E?5ѺѺѺE?5h^OE?5{h{hE?5E?5E?5E?5ѺѺѺѺѺѺE?5F=0F=0E?5տѺE?5ѺѺѺE?5h^OE?5{h{hE?5E?5E?5E?5ѺѺѺѺE?5F=0F=0E?5ѺѺѺѺE?5ѺѺE?5h^Oh^OE?5{h{hE?5E?5E?5E?5ǮѺѺǮE?5F=0F=0E?5§ѺѺѺѺѺѺѺǮ;5+ѺǮE?5h^Oh^OE?5{h{hE?5E?5E?5;5+̶̶;5+80$80$ɰѺѺѺѺѺѺѺѺѺ̶̶;5+E?5E?5h^Oh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5 E?5E?5E?5E?5 '!;5+OI?c]Swqg{E?5 E?5E?5E?5E?590!MD5aXIul]qż E?5E?5E?5E?5 90!w['!MD5o;5+aXIOI?ul]óc]Sqǫwqgۿ{ż E?5E?5E?5E?5E?5 90!w[w['!MD5oo;5+aXIOI?ul]óóc]Sqǫǫwqgۿۿ{ż E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5 90!w[w['!MD5oo;5+aXIOI?ul]óóc]Sqǫǫwqgۿۿ{ż E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^O 90!w['!MD5o;5+aXIOI?ul]óc]Sqǫwqgۿ{ż E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^Oh^Oh^O90!MD5aXIul]qż E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^Oh^O '!;5+OI?c]Swqg{ E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^O{h E?5E?5E?5E?5{hkbS E?5E?5kbSE?5 E?5E?5E?5kbSE?5E?5E?5E?5kbS{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^Oh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^Oh^Oh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#{hkbSkbSkbSkbSkbSkbSkbSkbSkbSkbSE?5{h.*#""".*#".*#.*#""".*#".*#{h{h{h{h{h{h{h}}E?5E?5{h.*#""".*#".*#.*#""".*#".*#{h{h{h{h{h{h{hyyE?5E?5{h.*#""".*#".*# .*#""".*#".*# {h{h{h{hE?5{h{hE?5kbSkbSkbSkbSkbSkbSkbSkbSkbSkbSttE?5E?5{hE?5 .*#""""".*# .*#""""".*#{hE?5E?5{h{h{hE?5E?5{h{hE?5E?5E?5ppE?5E?5{h.*#""""".*# .*#""""".*# E?5{h{h{h{hE?5{h{hE?5E?5E?5E?5E?5E?5E?5kk{hE?5 .*#""""".*# .*#""""".*#{hE?5E?5{h{h{hE?5E?5{hE?5E?5{h{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{h{h{h{hE?5E?5E?5E?5E?5E?5E?5E?5zfzfE?5{h.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#.*#E?5{h{hE?5E?5E?5E?5{h{hE?5E?5E?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{hE?5tata{hE?5{h{hE?5E?5{h{hE?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5E?5{hxn\xn\E?5{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{h{hE?5E?5{h{hE?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{hE?5{hrhWrhW{hE?5E?5{h{hE?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{h{hE?5{hkbRkbR{hE?5E?5{h{hE?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{h{hE?5{he\Ne\Nh^Oh^O{hE?5E?5E?5E?5{h{hE?5E?5E?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{h{hE?5{h_WI_WIh^OƳƳh^Oh^OƳh^O{h{h{h{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{h{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{h{hE?5{hZRDE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5ZRDh^Oh^Oh^OkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{hE?5{hUMAE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5UMAh^OE?5GB7KE:OH=SLAYQD^VIc[Mh`QndUsiYxm]|r`vcvcvcvcvcvcvcvcvc{hyfvc|r`xm]siYndUh`Qc[M^VIYQDSLAOH=KE:GB7E?5kbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5E?5{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5}nE?5E?5E?5E?5|nE?5{h{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{h{h{h{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{hE?5 0.+ID<& +(#++*"""    #!  %%%  kdV{h{h{h{hkcW{hE?5E?5E?5E?5{h{hE?5E?5E?5E?5{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5 &&&  D?6:5-    )%/-)""!  '''### UNCE?5{h{h{h{hE?5TNBh^Oh^O{hE?5{h{hE?5{h{hE?5{h{h{h{hE?5{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5  *(%3.%'" ### 94->;7'&&$$$  (((  E?5E?5E?5E?5h^Oh^O{hE?5{h{hE?5{h{hE?5{h{hE?5{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{h(((  .,)2,%>90@=9 $$$!!!&&&... 72+640  +++   ,,,E?5E?5E?5E?5h^Oh^O{hE?5{h{hE?5{h{hE?5{h{hE?5{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5E?5{h     &#    %%%B>8  &&& UNCE?5{h{h{h{hE?5TNBh^Oh^O{hE?5{h{h{h{hE?5{h{hE?5{h{hE?5{hkbSkbSE?5E?5E?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{hE?5{h   1+#,'  ###  4/)$!     kdV{h{h{h{hkcWh^Oh^O{hE?5E?5E?5E?5{h{hE?5E?5E?5E?5{hkbSkbSE?5E?5E?5E?5E?5E?5{hE?5E?5{h{hE?5{h{hE?5{h###""" ///,&  $$$    OJCFB>    }nE?5E?5E?5E?5|nh^Oh^O{h{h{h{h{h{h{h{h{h{hE?5{h{h{h{h{h{h{h{hE?5kbSkbSE?5E?5{hE?5E?5{h{hE?5{h{hE?5{h   LG=+&     :5.,)$   h^O{hE?5E?5{h{hE?5{h{hE?5{h   GA85/&1/,   )))    :6/2.)     h^Oh^Oh^O{hE?5E?5{h{hE?5{h{hE?5{h'''  """ 222$$$&# 5.&! )))  ?;5;71  ### ((('''h^Odzh^Oh^OdzƳh^OE?5{h{hE?5E?5{h{h{h{h{h{h{h{hE?5{hE?5E?5{h{hE?5{hE?5{h!!!   ###<6.A<4 962FB8GD@   h^Oh^O{hE?5E?5{hE?5{h{hE?5   30,!+(#    0-(PMG   h^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^OE?5{h{hE?5{hE?5h^Oh^OE?5{h 62+83,@<6    '&#   84/   h^OssE?5E?5E?5{h{h{h{h{h{h{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{h{h{h{hE?5E?5s{hE?5h^Oh^OE?5{hE?5{h{hE?5   +' % +($  ###%%$"! /-*62-*& !1-&2.(=:3>:4*%62,KHC'% #!87495/      taE?5E?5E?5{h{h{h{h{hE?5taE?5{h{hE?5{hE?5E?5{h   """     850NIAnh^_YP3-%)$ +'61*2-%KE=MG?50'B<4UQI@>::60       h^OE?5{h{h{hE?5E?5E?5E?5{hE?5h_O{hE?5h^Oh^OE?5{hE?5{h{hE?5UUU1-)hhg     *)),'.)!   HB;ZTLQLE>:4 %#(%40*;6.A<563/*'$       SK?E?5{h{h{h{h{h{hE?5SK@E?5{h{hE?5{hE?5E?5{h,,,TRO!,)%KKJ...    @:2(#  $"1-%RLD40+   #!    ...E?5E?5{hE?5{h{h{h{hE?5{hE?5E?5{h{hE?5{hE?5E?5{hE?5`__&"&#&"hfd   '''  GB:2.' '%"73-,(" 2-'A9/D?8,)& !!!   E?5E?5{hE?5E?5{hE?5E?5E?5{h{h{h{h{h{h{h{hE?5E?5{h{hE?5hfd*& *&!*& DA<  E@961,$" @><0-(:5/-(!   GA9@90("(# %$# E?5E?5{hE?5E?5{hE?5E?5---3/(/+#/*$/+#/+$LLL    *(&0.);6.=6,<6-71*B>7LHC30,   ---  333$$$ ($!JE>,(" QLF:6/3.)73/!431 %$#  E?5E?5{hE?5{h{h{h{hE?5{hE?5E?5RRR3/'3/(4/(3/(3/'llj,)#;72!/,(/,&;6/2.&'"'"83*ZTKMH?QKAIC:&"# ))(    ...'''!!! -*&72*2/*(% 84.B>7LG?ZUK93*50'=80($ "62-,)#;72SK?E?5{h{h{h{h{h{hE?5SK@E?5{h{hE?5E?5{h{h{h{h{h{h{h{hE?5jih93+93+84+93+93+igb!% =81?:494, ?;3/+$0+%-)#-)% 30,$ 82*<71     (((  &#(#51)"  '%#)'#C@;+("3/*YUOD?9!)%$ 51)83+!% h^OE?5{hE?5E?5E?5E?5{h{h{hE?5h_OE?5{hE?5E?5{hE?5{h{hpnk=8/>8/>8/>8/=8/VRL      )$1-)     @;4JD<"    *(%&$"(&$ taE?5{h{h{h{h{hE?5E?5E?5ta{h{h{hE?5E?5{hE?5{h{hE?5HB6HB6HB6HB6HB6HB6HB6HB6HB6c`YB=3C=3C=2C=3B=3JD:   (#     &&&0+%OH@/+& ''' sE?5E?5{h{h{h{h{h{h{h{h{h{hE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5{h{h{h{h{h{h{hE?5E?5E?5ssh^O[[[\\\\\\[[[h^Oh^OE?5{h{hE?5{hE?5h^Oh^OE?5{h&"&"&"&"&"&"&"&"&" GB7HA6HB6GA7HB6HA6GB7:99  IE>OIA"!  )))      GB840*    h^OE?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^O[[[\\\\\\\\\\\\[[[\\\E?5E?5{hE?5E?5{hE?5{h{hE?5999MF;MG:MF;MF;MF;MG:MF;\[[ C>6QLC  """,,, # @;2 ***h^Oh^O\\\[[[[[[E?5{h{hE?5{hE?5h^Oh^OE?5{hONNSK?SK?RK?SK?RK?SK?SK?pom   :5-RME   ### +(#73+$$"""" %%%$$$+++222{hE?5h^Oh^OE?5{hE?5{h{hE?5&"&"&"&"&"&"&"&"&"`_^XPCXPCWPCXQBWPCXPCXPCyws!!!  .)!1,$    %%%   +(%" !     !!!E?5{h{hE?5{hE?5E?5{hHB6HB6HB6HB6HB6HB6HB6HB6HB6mlj]UG]UG]UG]UG]UG]UG]UG{wp0+$      %$!  h^Oh^Oh^O{hE?5h^Oh^OE?5{hE?5{h{hE?5wurcZKbZKcZLcZKcZLbZKcZKzum  $" #;6-#      /+&%$!  """ h^Oh^Oh^Oh^Oh^Oh^OE?5{h{hE?5{hE?5E?5{h}{wh_Ph_Oh_Oh_Oh_Oh_Oh_Pysg  ++*&#2-$"   !!! 1/+;60JE@  !!!  ))) h^Ossh^Oh^Os\\\[[[[[[h^Os{h{hE?5{hE?5E?5{hE?5~wmdTmdTndTndSndTmdTmdTxpc ,)$B=5(%     61)JF@  tah^Ota\\\\\\[[[\\\[[[\\\\\\E?5taE?5{h{h{h{h{h{h{h{hE?5E?5{h{hE?5{h{h&"&"&"&"&"&"&"&"&"{h{hvsiXsiXsiXsiXsiXsiXsiXxp`-+&>8062-   """    #!gbZGC=    h^OE?5E?5E?5E?5h_O\\\\\\[[[\\\E?5E?5h_O{h{h{h{h{h{h{h{h{h{h{h{h{htyn\yn\xn\xn\xn\yn\yn\|qa   =7.KF?     ***XRL   SK?{h{h{h{hSK@E?5E?5SK@r~s`}s`~r`}s`~r`}s`~s`vc72)GA8&# $$$%%%!!!  @:2   """ E?5E?5{h{h{h{hE?5E?5E?5E?5E?5h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^OE?5&"&"&"&"&"&"&"&"&"pwdxdxdxdxdxdwdzf   3.(LE;&!   $$$   *&"60(-)#   %%%E?5E?5E?5E?5E?5E?5E?5h^O{hE?5E?5h^OE?5n}h}h}h|h}h}h}h}h *** &"mg]ZSJ   ### ... """74/WQI85/   E?5E?5E?5E?5E?5E?5E?5h^O{hE?5E?5h^OE?5nlllmllll   JE=GA7,,,### ###  95/WQH%####   E?5E?5{h{h{h{hE?5E?5E?5E?5E?5h^O{hE?5E?5h^OE?5rqppqppqp  !!!EA9JD<-' &&&  -+'D?8:5/ SK?{h{h{h{hSK@E?5E?5E?5E?5E?5E?5SK@h^O{hE?5E?5h^OE?5{h{h{hh^O{h{h{hh^Oxtutttutu  -*%$!,("    ''' #"  """h^OE?5E?5E?5E?5h_Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh_Oh^O{hE?5E?5h^Oh^O{hh^O{hzxxxxxxxx   "!!:72+'!"   B?<+%"   ###tah^Otah^Oh^Oh^Oh^OE?5h^Otah^O{hE?5h^Oh^O{h{h{h{h{hh^O{h{h||{|{||| )))-*# ###-,*TPJ&!.*% &&&  &&&"""sh^Oh^Ossh^Oh^Oh^Oh^Oh^Oh^Ossh^Oh^O{hE?5h^O{h{h{hh^Oh^O{hh^Oh^O{h &&&72+KF>!  $$$ !!@<6      $$$h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^O{hE?5h^O{h{h{h{h{h{h   +++.-+GB:TOG!  +++   &$!,)#    ***"""h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^O{hE?5h^O{h{h{h{hh^Oh^O     *&FA9-+& )))!!!***$$$ C@:$"   h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^O{hE?5h^O{h{h{h{h{h{h{h$"     3/)OJB=:5!!!$$$       $" h^O{hE?5h^Oh^O{h{h{hh^O{h{hh^Oh^O=9341+-)#"-*$FB<63,)&   '% NIAGA8?;4 ''& ,,,  32/ " 21/  )'"+($!83.61,>94/,&"HD>=9341+E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5E?5h^Oh^Oh^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5UOFRLCXRITOF_YO]WNSMDYTJ=8082)("A;3?92GC:B>7JD9WPDhbX?;42.)DA<1.*(%"&$"  =9463/ 63.A=6%!+&,' '"VQHWRJ4/&/)!RLDD>6JD<.)#:5-:4-UOFRLCE?5E?5h^Oh^Oh^Oh^Oh^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5²±²±²±²-)$@=7EA<:6073-=9362, *& ($C>7;7/%":5,_WKVNC`ZQSMESNF^YQ>9240*51,0-)!  VQJ>92!,(!"61*@;3^YPF@7C=482)94,;6-D?7HC;-)"%!84-2.':60960@=6-)$@=7ME;ME;h^Ossh^Oh^Osh^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5{yuƵƵƵƵƵƵƵ ""!$##   LG>60'HD?+(#!=9362+A<4D>6C>7#1-'1.*430   E@9>90;6-=81.*#<94&#42/1/, ##"  VOBVOBtah^OE?5tah^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5igeʸɸɸɸɸɸʸ  $$$ +'+'=<9&&%  52,B=7YVN%#   $# '$     !!!  c[Kc[Kh^O{hE?5E?5h_Oh^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5SSQͻͻͻͻͻͻͻ}     72,  B?7*%! %!=81LG?'$# &"     qfVqfVh^Oh^Oh^Oh^OSK?{hE?5{h{hE?5SK@h^O{hE?5h^Oh^O{h{h{hh^OE?5{hh^Oh^OE?5:::ооопоооeda     <6. """   !=9272);5,RLC62, =94)&! ###'''    tatah^Oh^Oh^Oh^Oh^Oh^Oh^OE?5E?5{h{hE?5E?5h^O{hE?5h^O{h{hh^O{h{h{hh^OE?5{hh^Oh^OE?5 <;;  94+4.&!  #!,($FA4kbSjbSC>4h^O{h{h{hh^OE?5{hh^Oh^OE?5µĤģģģĤ&&&    <7.+&31,     +'    &&& vvE?5E?5{h{hE?5E?5h^O{hE?5h^OC>4C=4E?5C=4h^O{h{h{hh^O{h{hh^Oh^Oƥƥƥƥƥ˽ 864WQH3.%63/$$$   +'!   !!!  E?5E?5{h{h{h{h{h{hE?5E?5h^O{hE?5h^OE?5E?5{h{h{h{h{h{h{h{ȧȧȧȧȧ     \VN0+"$! """""" ;6/2/+"""       SK?{hE?5E?5E?5E?5E?5E?5SK@h^O{hE?5E?5h^OE?5E?5{h{h{h{hh^Oh^OZYXȧȧȧȧȧ   <6..(     )$962(''      h^O{hh_Oh^O{hE?5E?5h^OE?5E?5E?5{h{h{h{h{h{h..-ƨȧȧȧȧSRQ """   888C=5A;4    1,&/-*  h^Oh^Oh^OtaE?5h^Otah^O{hE?5E?5h^OE?5E?5E?5{h{h{hh^Oh^O{hh^Oh^O{hȧȧȧ    %%% 61*0+&3/,         ###  h^Oh^Oh^Oh^Oh^Oh^Oh^Osh^Oh^Ossh^Oh^O{hE?5E?5h^OE?5C>4C=4C>4C=4h^O{h{h{h{h{hh^O{h{hqolȧȧȧ  GB9MG@>:5  #!     h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^O{hE?5E?5h^OE?5C>4kbSjbSC>4h^O{hh^O{h--,ȧŦQPO 0.+ID<& +(#++*"""    #!  %%%  h^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^Oh^OE?5{h{h{h{h{hh^O{h{h{hh^O`_]Ȭ &&&  D?6:5-    )%/-)""!  '''### TRUEVISION-XFILE.mmpong-0.9.1/resources/CEGUI/imagesets/vanilla.tga0000644000175000017500000004245611130511363020741 0ustar andreandre   444$$$444 999kkk444&&& ...999===999... """ ___444111lllqqq111 """ """ qqqqqq 444   EEEggg ___bbaEEE ...111444GGFGGG888 444999999 ___FFF ====== ===AAAyww444===AAA655====== !""  444999999___  ___......444 rrr  ONN  qqqqqq 444 SSU 111lllqqq111""" >>? $MMM ...999===999...  /./   ***000999___    ___lll555 555lll444444 /.-  444444  ___ ! """______"""  ______  '1:@DF D@:2' """______"""555555  ':M^jsxz xsj^N;' 444lll555 555lll  ,Gd~ ~dG, ______555______ 4Hlց mH(444lll555 555lll444444  @d e< ___lll___555555444444 &M}O(KKK===555______"""______""" /[_2 ______"""______"""7fk; 444444 =or@nnnMMM555____________ AtvC  ***000999___lll___444444 BuvC___  ***000999_________ 444 ?qr@___555nnnMMMKKK===444:kj:  ______""" 1^^1___   ______""" 'N~~N'______KKK===444nnnMMM444  ;c c: 555_________  ***000999___555______ 'Fkց kF'lll555 555lll444444999MMM444  +Fc~ ~dF+ _______________000___  &:M]jsxz xsj^M:' 444***===  '1:@DF D@:1'   """______"""______  ! 444$$$"""______"""______  999kkk444444555555KKK&&&444444lll555 555lll______444444nnn  nnn444  """______555555______lll555 555lll 444444ggg .9=A=9. 444444555555444444 1lӂq1"""____________"""______"""______""" 5 ...999===999... 5"""____________"""______"""______ """ q111lllqqq111q 444444444444 qqqqqq 444  444____________.1444$$$4444449...1119999kkk___  ***000999_________ =999999=&&&nnnMMMKKK==========A 444  A===AAAA  """   ====AAAA  B  fEEEgggKKK===nnnMMM=======A444$$$ _________  ***000999___=999999=999kkk4444449......9&&&____________... .f444444444 qqqqqq   B.""""""______"""______ q111lllqqq111q   EEEggg"""______"""______1 ...999===999... 1bbaEEE T4444445555551lӂq1GGFGGG888 444444lll555 555lll .9=A=9. FFF   444 444yww<======<ywwCCC444444655<======<655"""____________""" !""  .<======<!"""""____________"""  # #B444444 rrr **rrr______555555______ ONNf0͂0ONNCCCCCC444MMM999999MMM444 SSUB0++++++0___000___llllll___000___ >>? $f111111$===***555555***=== /./   B    #*   KKK555555KKK /.- /.-______llllll______  ǵ 444nnn  nnn444B  # #______555555______444444++++++"""____________"""*++++++*"""____________"""*ǂ*CCCCCC444444 **444  444##BTRUEVISION-XFILE.mmpong-0.9.1/resources/CEGUI/imagesets/Vanilla.imageset0000644000175000017500000000267711130511363021725 0ustar andreandre mmpong-0.9.1/resources/CEGUI/imagesets/TaharezLook.imageset0000644000175000017500000004650511130511363022560 0ustar andreandre mmpong-0.9.1/resources/CEGUI/imagesets/Imageset.xsd0000644000175000017500000000264111130511363021064 0ustar andreandre mmpong-0.9.1/resources/CEGUI/copyright0000644000175000017500000000240111132477741016567 0ustar andreandreThis copyright/license notice applies to the files in the CEGUI directory. Copyright: Copyright (c) 2004 - 2008 Paul D Turner & The CEGUI Development Team License: The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mmpong-0.9.1/resources/CEGUI/schemes/0000755000175000017500000000000011354734770016272 5ustar andreandremmpong-0.9.1/resources/CEGUI/schemes/GUIScheme.xsd0000644000175000017500000000535411130511363020551 0ustar andreandre mmpong-0.9.1/resources/CEGUI/schemes/VanillaSkin.scheme0000644000175000017500000000565511130511363021665 0ustar andreandre mmpong-0.9.1/resources/CEGUI/schemes/TaharezLookWidgetAliases.scheme0000644000175000017500000000503211130511363024330 0ustar andreandre mmpong-0.9.1/resources/CEGUI/schemes/TaharezLookWidgets.scheme0000644000175000017500000001504611130511363023217 0ustar andreandre mmpong-0.9.1/resources/CEGUI/schemes/TaharezLook.scheme0000644000175000017500000001576511354706475021722 0ustar andreandre mmpong-0.9.1/resources/CEGUI/looknfeels/0000755000175000017500000000000011354734770017004 5ustar andreandremmpong-0.9.1/resources/CEGUI/looknfeels/Vanilla.looknfeel0000644000175000017500000015315411130511363022262 0ustar andreandre
mmpong-0.9.1/resources/CEGUI/looknfeels/Falagard.xsd0000644000175000017500000005001211130511363021202 0ustar andreandre mmpong-0.9.1/resources/CEGUI/looknfeels/TaharezLook.looknfeel0000644000175000017500000073027511130511363023124 0ustar andreandre
mmpong-0.9.1/resources/CEGUI/layouts/0000755000175000017500000000000011354734770016343 5ustar andreandremmpong-0.9.1/resources/CEGUI/layouts/GUILayout.xsd0000644000175000017500000000520311130511363020664 0ustar andreandre mmpong-0.9.1/resources/CEGUI/layouts/VanillaWindows.layout0000644000175000017500000001065111132372353022533 0ustar andreandre mmpong-0.9.1/resources/CEGUI/layouts/mmpong-gl.layout0000644000175000017500000001414111354706475021501 0ustar andreandre Server: localhost Server host name or IP Address Port: 1212 Server port number Sexy false Enable sexy motion blur rendering? Sound Enable annoying (!) sounds? Fullscreen Enable fullscreen? Quit It is not wise to click this! Cancel Hide me! Connect Click here for instant fun! Nothing to display yet. Nothing to display yet. Nothing to display yet. Nothing to display yet. Nothing to display yet. Nothing to display yet. Nothing to display yet. mmpong-0.9.1/resources/man/0000755000175000017500000000000011354734770014562 5ustar andreandremmpong-0.9.1/resources/man/mmpong-caca.60000644000175000017500000000145211132727444017027 0ustar andreandre.TH MMPONG-CACA 6 "January 12, 2009" .SH NAME mmpong-caca \- basic console-style mmpong client based on libcaca .SH SYNOPSIS .B mmpong-caca .RI server .RI [port] .br .SH DESCRIPTION This manual page documents briefly the .B mmpong-caca command. .PP .B mmpong-caca is the basic console-style client for the massive multiplayer pong ( .B mmpong ) game. By using the caca library it is playable in a terminal as well as in a X11 window. .SH OPTIONS .TP .B server The mmpong server to connect to. Accepts hostnames and IP addresses. .TP .B port The port of the mmpong server. .SH SEE ALSO .BR mmpongd (6), .BR mmpong-gl (6). .br .SH AUTHOR mmpong-caca was written by: Kai Hertel, Steffen Basting, Jan Friederich, Rodolphe Prignitz, André Gaul .PP This manual page was written by André Gaul . mmpong-0.9.1/resources/man/mmpongd.60000644000175000017500000000444711132727444016315 0ustar andreandre.TH MMPONGD 6 "January 12, 2009" .SH NAME mmpongd \- masssively multiplayer pong daemon .SH SYNOPSIS .B mmpongd [\fB\-\-model\fP=\fI%s\fP] [\fB\-\-paddles\fP=\fI%s\fP] [\fB\-\-address\fP=\fI%s\fP] [\fB\-\-port\fP=\fI%d\fP] [\fB\-\-dev\fP=\fI%s\fP] [\fB\-\-background\fP] [\fB\-\-nthreads\fP=\fI%d\fP] [\fB\-\-max-connect\fP=\fI%d\fP] [\fB\-\-max-per-ip\fP=\fI%d\fP] [\fB\-\-max-payload\fP=\fI%d\fP] [\fB\-\-min-rttm-interval\fP=\fI%s\fP] [\fB\-\-client-timeout\fP=\fI%d\fP] [\fB\-\-verbose\fP] [\fB\-\-help\fP] [\fB\-\-version\fP] .br .SH DESCRIPTION This manual page documents briefly the .B mmpongd command. .PP .B mmpongd is the server for the massive multiplayer pong ( .B mmpong ) game. It is capable of serving hundreds of clients simultaneously and scales sufficiently well on SMP machines (pthreads). .SH OPTIONS .TP \fB\-m\fP, \fB\-\-model\fP=\fI%s\fP Game model to play (defaults to "linear") .TP \fB\-r\fP, \fB\-\-paddles\fP=\fI%s\fP Paddle profile to be used by all teams (defaults to "flat") .TP \fB\-a\fP, \fB\-\-address\fP=\fI%s\fP Local hostname or IP address to bind to .TP \fB\-p\fP, \fB\-\-port\fP=\fI%d\fP Local port to listen on .TP \fB\-d\fP, \fB\-\-dev\fP=\fI%s\fP Network device to bind the socket to .TP \fB\-b\fP, \fB\-\-background\fP Daemonize and return .TP \fB\-n\fP, \fB\-\-nthreads\fP=\fI%d\fP Number of threads to run simultaneously (-1 == auto) .TP \fB\-c\fP, \fB\-\-max-connect\fP=\fI%d\fP Maximum number of clients to serve simultaneously (-1 == unlimited) .TP \fB\-i\fP, \fB\-\-max-per-ip\fP=\fI%d\fP Maximum number of clients to allow to connect from one host (-1 == unlimited) .TP \fB\-l\fP, \fB\-\-max-payload\fP=\fI%d\fP Restrict traffic to specified amount of kBytes/sec (-1 == unlimited) .TP \fB\-s\fP, \fB\-\-min-rttm-interval\fP=\fI%s\fP Minimal interval between near-synchronous packet transfers (-1 == unrestricted) .TP \fB\-t\fP, \fB\-\-client-timeout\fP=\fI%d\fP Timeout between status updates before client session is terminated, measured in msecs (-1 == no timeout) .TP \fB\-\-verbose\fP Log more messages to the console .TP \fB\-\-help\fP Show help screen .TP \fB\-\-version\fP Show version information .SH SEE ALSO .BR mmpong-gl (6), .BR mmpong-caca (6). .br .SH AUTHOR mmpongd was written by: Kai Hertel, André Gaul .PP This manual page was written by André Gaul . mmpong-0.9.1/resources/man/mmpong-gl.60000644000175000017500000000157711132727444016552 0ustar andreandre.TH MMPONG-GL 6 "January 12, 2009" .SH NAME mmpong-gl \- graphically advanced OpenGL mmpong client .SH SYNOPSIS .B mmpong-gl [\-\-sexy] [\-\-nosound] [\-\-help] [\fIserver\fP [\fIport\fP]] .br .SH DESCRIPTION This manual page documents briefly the .B mmpong-gl command. .PP .B mmpong-gl is the graphically advanced OpenGL client for the massive multiplayer pong ( .B mmpong ) game. .SH OPTIONS .TP \fB\-\-sexy\fR Enable motion blur. .TP \fB\-\-nosound\fR Disable annoying sound effects. .TP \fB\-\-help\fR display help and exit. .TP .B server The mmpong server to connect to. Accepts hostnames and IP addresses. .TP .B port The port of the mmpong server. .SH SEE ALSO .BR mmpongd (6), .BR mmpong-caca (6). .br .SH AUTHOR mmpong-gl was written by: Kai Hertel, Steffen Basting, Jan Friederich, Rodolphe Prignitz, André Gaul .PP This manual page was written by André Gaul . mmpong-0.9.1/resources/win32/0000755000175000017500000000000011354734770014751 5ustar andreandremmpong-0.9.1/resources/win32/mmpong.rc0000644000175000017500000000005211127716377016572 0ustar andreandremmpong_ico ICON resources\icon\mmpong.ico mmpong-0.9.1/resources/sounds/0000755000175000017500000000000011354734770015322 5ustar andreandremmpong-0.9.1/resources/sounds/pong_1.wav0000644000175000017500000001056011132614366017216 0ustar andreandreRIFFhWAVEfmt DXdataDrnic^&[ YXZ^q;>ADEFGGHJrLOMTY]`a<_YN?c-UGOžܡЪG~ ׷Dɡ )º Q ˴~K;ݟ?槭eb&g9jHSZ!^^[WRNJGPF{EEDC+B?+=':64:3235q8x;d>@qBC5DD/E]FwHKO)TXf[\&[VL?.0- ɹ]u;Y9/ӸxYƻ;sxNɩ#̓feVQĻ'I뭞E¤7 !K3B NUgYsYpWS|O5KGD7CRBA!Aa@?=:7420}00247:=>3@@EAjAhB6DFJN$SV9XTWRJ>/rq Οf񧮩"NԻվN^vŰr Ρ3Ю#U[TҶW~E&@g-n<HO%T%USgPULGFDA?>[>=\=6>b@BEIM@Q6SROH*>80<  ԤGt-tƧ@6ћJC3ϧ ʜǿmĵK˾ͷ۳fZe`$39)(6rB0JNMP@OwLHDIA>uAEHfL$NNNKE:75443e321/-6+)U'K&#&&Q(\*,.X0123H334C68;>ATD6EC?8.B"yvSHˢCƺ}ɿ V$k n5QVں\سֈjҒ"]3ۆ2ȯjTsxaΣJЛgXҸӉՐلۦݛrٻ׻҇EAtwn+J k!1,|4:==x<:6381I/.Y--,E,r+)#(1&A$"u! Y!d"#%'P)*+ ,Q,,:-g./F2 58t:;v;831,!?B!KͨǗ7;f:͘ψ҄oԚgD$moz)MٔFZԑԞ YU̍!Ǵx.Է, D'/47875I30 .%,*)z)7)(%( '%#! `f "U$%P''l(((o)d*+.0^3Z56640*6!L {ڦͿɪK)˱ͽ"IdךדئS R~ 3A1|1׃׫9ՙ$я=̹ʍ.z aB""*/3Y4l31`/,*(_'&=&&`%$#" Mq, e"#$%c%%%u&')+j.0922&1N-'RR D߃QΗqϤZ4wُ'lމ* 5V۠@ە^٤yпVvЋӰe :y%*-d/8/-+o)&%##"}"7"! .fK- y!!!I""#%D')+0--, *N%) !׭"%ӹڿ*_ޘVp@[*djޤnoYԽv֖7a %(f**q)'%#! "Y|L0'UQ!#% '((M($&H"H B\Qm? xo[^ߍSq u2g U;޷`٠ iޠhqY^  $%%&a%r#!=Q:dP"q*n'?=\ ""#]$${"W:  t*}'ܹ]ܹj'Z!6TPRO- Uݱ&UO H N! ZxW!Wf}){ClqUJra@\ F3%]j oX'ddyN((Wa bq:n/w,-{sCkr"L8#%NP  Q/Uv<CMiO9>Az4g|t=^] ># xd{}R4i` u = d"%e8<7R7 xV0Js:j[I&u PxX>u+%09/4, Y b   y ( R p  ! X T%[#i   !p]yU~.)5PrdT;@A BwaKR  Y X t f Q _ q\vtE ` P }  * /,DfT8r A\vARFe tGtkHP?i0* F K1]:"k&-Flu>!9t*'VGz'qn4"B5p~Wt070,rV ;WMz+ b&Y mmpong-0.9.1/resources/sounds/pong_2.wav0000644000175000017500000001056011132614366017217 0ustar andreandreRIFFhWAVEfmt DXdataDrolgb7^;Z{W[V Wt57q:Z=@PBCDEEzFGmI>LO7TXL\^^g[UK~=,ҺYH\tͭбf'Sĺμ3MǞo[=tƞ(Ծ Bkf[J*ˢϤU9a2 o/A?KTYu[Z0X^T&PLH&@jAA>BB\CDFJMRUXYXITLbA2!R!><:q8530O/. /N0V24q79;"=>>>g?]@ BDGXK?ORTTIRLC7~(ߧ?䯽ˮmTؼ?P¬ËſC ϲЍѬѫ΍ʊu95VQز\򬸬t2G7*68UCPKPQhQOKGC@>2=f<;;;:8>63`1/T-G,),,.02D5f795::@;;BR *ٟzwر򴒸,FCHžŕ tpӒҜD˲DʼnIο uϾ 2 .x:CIL1MKH,EA|>;@:.98W876541/_-n+*))*w,.0(34R6 77788U:<3B( Z 5 ysm ƽX>Sɿʎ>̧_yuҎԖ9Nٞ.؉ւh3̍̍l`d_ ( 3:@BLCB?<96421Q1 100/-+)~'%$$Y$I%&(*,..//Q000124n7: =?RAtA?S;~4(+-{N7*A̻MϚ2еДҔ}u1|'Wܔ;o؂֘ҾyЅ5Рω̇%?d-/p*#3M9=>&>m<963}1/..-q--D,+D)y'%#u"!!"C#$}&Z()G+),,,8--./1i4379;<ޙ>%ؽ׃Biy͹ʝɩʚ͘ұZ##C+034P420D.+)s('&`&&&%.%;$"e!L(4 "s#$H%j%%%F&'A(**,.0(22Y20+%d!ߐҀό_aM|Kpܚ߉.:'ݧ`ړP;ٷ׼ՀGxkΦebg ;$)t-v///\-8+('i%#P#"""""b!c Tc2h >!!"A""##B% '(*,-.,)%u ޚ!`QӷfیKݬeSGg$]KvMuߵޠ4}NܺؾԈӇbԸڈl #d'*+*)'%# " ^3jKF05w~s.u !#%^'(o)('#I?!/ߖظ֬֎!z޲ߑ[m*erj_BWl9m ܥ1W؀ٻA_I!f$&h&%#H" AgiIOPkpA6HU "#$$4# 0Z kRn J۵_ ; 6T#21@^;Wzn_ݪ݁ߩ( F ,! ! 5t TB|a68.A3`}4 eL('NMI DB4;sk n8q-(X_CfvMv}bkIi }2yg1<@^!SxZXrS\R !FUmf.$SeZ\q#^f+. %qp0? 1 X N z ?H^}* g(j~LJ>!< "^1n #?#M~w5*]q/zGW _u n J = 2 , = 4 5 a*(=[O 0 35,qi!Fh|6gQSe:K4Ckv_~_ q Y  L " t 1 v U:\ 8   $ : i # G  k l C(/~3%!.@.ama4,>3Su;?A p?3eC/$nh[E l0_z4c*QF,*. #1+Gom/hEmmpong-0.9.1/resources/sounds/score.wav0000644000175000017500000012613411132614366017153 0ustar andreandreRIFFTWAVEfmt DXdata0WR1LSqR We][T*ON&LGCE@<9962,/+'_$ ?tEN)_"%o)-0369<>?A?BAC1DEE|FF4GGpFAEqC@=!:5O1v,v'u"W E{t>)wAQzX[ B !"%),/134-5"5432W1/$.R,_*:(%A#` 8GJ AmS} fS # 0:?!$(,r/2469<@CE2HI)KKHK/JbHEB?; 8M4x0,*)%N" JGn 1 w3 #'c+.145b7?888~87E7\6I53c2n0!.]+(m$C  ;V(3_,޷yքLҍҩJiْjJ#Njh& n .{jQAtx3BoҠРؠ,&'^0?`ś5cDؿخ֍}˵CK 7%g(ShԖ 񟌣WB䲃fxʃ+vLXW7t: \9ՙn`ɓJr˳ԭ]4VN7FTT(ѹ"D2DqKko*@ALd/ڵΎLyӮ']O]LM?Ʋb5h¶Ʈүcڶݨ)5tI[/ސjYɠ}d:ݼp|u\.5żtAfuRl< t-{   Z <h3MD'jWT# vJ "'+.`258;M>@wCEGIKKHLKKyIZGDA>>;7c41-*u'^$Q!@AVxaL ] [ S $)GQ=#'+.2D6o9?@cAfAAW@w?p>Y=-<:98?6,41.+'#d 4s6APVKPV79\ (d R#&),x/1345543,2 0-3+u(%" 9Xt}vz @X&|" 4x4$(H-s1H58;>LACEGIKM9OyPDQQ2Q+PeNKHHEEA=8[4D0s,)&o#V!Ukh!P"#%&P(C*,3/I259>BpG#LPTGX[$]R^^X^F][YDWT-ROL9JnGDuA'>:62.*& #q0Fw)(I!#%'*,$.#0.2:4G6C8":; ==+>=0׵h[TjõǶ1$(˙Ԓ[^ s7fx5+8(pڼ,ҿk̈́FίϴCJרU''<9274\2/@-*'$!e!x8!%(*-023i56789`: ;;;<;;9185)3/V,f(S$) i  kaGwwy }%"N&)+- 0^204\555s5{4,31/- ,*(&#!BsrAU  \   T ~ R!&k*.L37.<@DHKLOhRT"VVV$VTyROLXIEBq?< :76p4310d/.,H+)>(&F%#"!9! ,!!#$&)+.D259h",xG̾~J ǯάTXЫ iɓƂT  Fs"Om>DpKRܕߘàiTf~bbb22F"ی۟`4_}؁gLc#[}ʟņ30th yX޺ls +ŁMC@.l_3tAHw (F<eʩxė zֺ:xɿ ^Yڹݭ?=tDw3R\nݘ~ZӍϻΫpʉRTʞEO͵ Խe(J! U<k V | d an?:yf6kCwA Z z L#&(+.k1^4=7:<?@]B0C^CBA@==;N8G5%2/#,i)&$g" akQ2Rm] y"$&)f,/26Z:%>AsEHKMOP6QQPOBNL(KIGFjDB@L>;852. +O'#rOt   zA/CQgfF!"5$%&A(o)*+j,,Q-@-,+*(&$%"Cb>o 6:}y? W % *-0t2333210/ /.m.../0000{0/.-;+4)&~$"xjc7~p)? #*&})-0495=PA*EHKNOPPPON'LJGEtC[AO?K=F;'9641"/.,)%"dZ^zp"8%'1*h,c.01374c5678:; <<]=k=<;O:8w5Z2.D+'#3 <* M X/&Md b Q v b*1XS "$@&g'!(N('&$M"Q< Bd do{F;b&e'm: L!$%&0''E& %g#o!+T{}W t ="7q%@D . $$K5c zvAGt?ߚoi֏#SA˰˘ayfב؎Oc۴uۊ(\u}_*/bYˍǼ I'h gA͓QΗ:~eT̴klň'hãŒe^V?ERES'wG ɳIӪT dեuR81K Ć~̆Јo܉ߞKb4D) ~#R.єάJɉ0ąu=6ȥz͕O fb"86,nzOw}!j0?*$݃.)Rܟ8jp^0ܦzzܽQNުxA3xf0 G '5b @ "Rr~/uL޴j^GE,Qxzh%% $hc!;$&L'&(((-('&n%#@"C 4Z e,c6W! A!*$;'8*2-40=3[69>o@DBCDTEAEDSCAU?<9641M.+C)'%P#!8  z>pj- !$&`){,/u3%7:e>AD8G8IJK LLKZKJ5JI0@B/CxB@p=I9N4.("^q6  ?Ie;L#) y ;!4! bz"Y1 3 LcT^߾ھـڝ%KzKK- . C; n +@ 1?2/)XI)II -bܲ&ژڨ۬ۥOx>>ahhTm'="ڰܗ޳Hg <5 B]  i_YNQ(mLiS۰د֋H ֵf'^ vqP,xCpEl X Z$gtjݡwjߞr 7x Qt  3%| !"d##$$#"! .2 { ! eaZ { 1 R w"%g)-04<8;>:AlC.E|FmGHNHSH"HGFFDB,@"=r9)5X0+%jA 7~ Wd!=%(?*+,+*)'*&$T#""#P%'+.2t6:=1?k@@?e=B:-6d1,[& l Sc4$& M G8"`&),/ 2 45678 887w7765*5I4031M0}.d,*'%" k_p` [ 5#%(*,v/24u7`:h=}@CFpIK NOP@Q2QPO N_LJHFDC[A?=9<;7b4i1.,)+ *V)(((x('&t%L#v Q $WS _ A"&&)e,."000V0. -*'p$ hU BgyCUv݉ٛ؄ףeի՗V)B݉nU vDrm p (Rٷ2Ω̵.]Wϱ1wA4Rۨ7$wD'0 3 N -~-* g Hے$^\&5kf*̱΋rP%fP߈]Hر `٤ےlB8Y JЪ(2väWvثJF$ĵMĸ?໧¢ąDžʟ͵и} -L-ߢߣ@|l܌bզgΐ;ʑtƛĄ=ƽAgfIJѱ`3]<\d=hUס,ްDtPyeP@b{5@XݐABəŸč&ǖmn,]{ U,3 ? Z(@5BBzBSAc?d<840+'l"1&"@b1 !-##,$$#"}"$"<"" $%u(+./H3N7-;>3BE?GHIIIHFDHBM?;I8`4C0;,(#SCO~QY}f*!0#$]&'Y)*V,-/)124 697V8#99{9876420A.+q)'$"[ 3va_y ; B C _ +o? "%(B*J,./02-3$4 55i6!777776531/,)&#I $3u F  #z W h uDS!%)_.26:>ACEuF~FEvDBa@>;986[6l667 9+:;;{;:963/+G'":d^;DI"%q),g/1345}66666x6H665:543T20.J,)&{#3  M J{-JOF(L!\]q?eaAs:};a73O؆)Ӛ͊̏X8x@jg7s=9DR4 KՁӴѧFРЎԼ֥jۼ% u܃S=@:R]*=Z/ zw@Zl޿)Lv)ͥkH@mǞǶ7?ʼ\)ЫBԺ ٢^ܜmA|Oۮ,ͮʉljmoƻ̸?;*OoױmJD:pw0ЕҟK֥שg1OKmS؟׼ֹvEB;Iį3ȹ έ.'g׼Kv|-f muefMxNR 9LOڪ #? >ZX pf<& $ c qkB;AHmop ] P 5  OGUvuaUn 7h^ i <C }sVWM":|?%;m< >~ "#$#%%$#"_!P0L  aIj) % ` ~ B q   !;!"'j,:159m=6@BCC7B@ > ;7*40P-N*'%#""!!!!!!!!!h!+! | m m !O"T#$ &')r+d-L/124.5667L7w77778W8888838[76v4i20U-r*{'y$!&J=\4 V)!m#%'*+-./0 0/.-@,*(5'X%s#w!k?zCP m iBkmR}x , : "!$%%%%%%%f%%%&'3)*,.s02v3v444:4271.,(e%!XE5  { IWQm!t$&(+,.L01234 565 54e43Q32x2?282O222232210/B-%+([&#j!$M6S&eH)b ;"y#$%:&&&6&j%5$" C4@ MA-}dNJbeApe0ݦۣق<o4AMB͉{eiˇ͉&%՜YMRI^6 ?PnH?~ Jٍuj}ّ٘وKD}׈ցvԁӺ3$Ҭҙ`ݕ< 2&} ! ?=Bތܷڧrhѯm Y-P,$O¦Ê]8"&YɹFӼՏ$}ڇ6܆k ۺUѷ@ɓ ĬۻHḐGгvr)t *ٶ\˦Vbum\J0_ .rFVeeI6 ".%'n*,/0l2v3 4,43Y3210y/q.-,+*)('M&$" !@=:/m{< m "#$$&K'_(r)*+,.0/[0V1!222g21V0.y,)F'd$~!>z- & q  9 1 J#jN_HyLt !#%(*+Y-:.h.-e,+*&'#Y` R o5)bY U <EP3$!T$&)+-/01T2u2H21Y100H/.--',;+.*)'T&$T#!W U'k~W "$ &(1*+, ./11m23V455966I77S889.9987z642c0-*'$!pwf}  } _} y v e G &j^ fj& jIJ[nk_bknk@`^ۂ.;|˞дAzx i!Rdߛ߬mi~DZڤcυwsȈƣƓƱ3NjL^!F̰Z=EpաݘUEDN ^FݏU֨ӥEΥWʕȘŗ7-ٿgFeǦ:kt]֠Kuۅ{_(c2b޸g Ԙ9͞ˍURŽęărƀǩȼɳjQ*ʢ:"ɛ[[ˣ->ԧ.ٺ?ޖi\kmz#e@Mnjm‚)·r]Xδ#ZϺfeҔO%6hl}Gm>H!N05lxg4Aa ?6k}> ""v##}##D"T!- B|2AN U D e = Xs < , < u =H<5;5+pN8FQmv{  ng5tn,uXd6 S7m:h*9,j^v* T?`Tba  @S`dfkosnO "#$%,&C&%1%$""!kr34g ! P  & z ,dg!$&)+,4.A//[0000000001012100/.-D,*)U'%?$"!! E . ? m 7!^!s!X!!  XV?#O!+$s'*.2F5792;;:9;7?40,({$d Ca#>aFKp\+ 27 < 3A8!y#%')a+,-.)//.-J,*(&C$!~eqyAlFg3a}, "I%'w*,"/1244t555W54d439322\10/.|- ,e*(&a$6" T ^Le~ 8J?fw"Mwo9~}hAz  qMmV@trD.CR-<`D{Q@r ֶdɵվ ȻXĸ K`UѶ1Ԋ 4O]YA ޺:ߐߴߜEߧݏPM զ%Ф2ŞÏV"4Y&NNĢF9p]r"; ަ*or6,a:N 5@$ڬj) ;Ԙ(ҶҢҋxGt;:ѤрӢ׻StQ*Oۄ~ H<JFզԣ Цˈ''ě¶z'#s!Si@O^t(3hޭzxfcҘ9$ !!!U!B 8 sTHM{#)Z x]}PPS,LcC !"","%!\D hoeK.P6cOV 1!4ePZqqU:t X [batL bK 3^W!#?&(*H,-.T///T/.e.-[-,,l,O,@,4,,++4+*)('&%%#?" R{oQDU 6#%(+.14e790<>?@+A%A@a?=p;852/,G*(9&$$###N$$$$ $#!xv  " E ;NbO#&),E/G123<4:43 320i/-q,*A)'%#!/sB h `t_+9z~ = "cv4j !V#v$F%%%%"%C$-#!t op4yX v X N scEEr?  e 6/XsowmYo! #$%&'''&%"6 bx[/  $S>RX'1-5 MQ#45EE G `\$p( f  Ihv@K5dx`ՎԸ"қґҘҺMy[|n6$1;I7~*MO)߱(܇ں֠PIͲ)ŪANҿ&Ðiǵ+u}5ۘSJ~izvvRA3Io۵T٘ֈf҅x]M^ȼě3^Jͭdbs܌}@  VUf$YM$N>׌xӔϟΨ̈ͅϵe6?t߷#-'XrZNc߉z.Ճӻ/t\,HƱ l&%,* ٙLޖ>:x\O44{u@yWNzLtN="0hY+6_gEf o (/UuU I!mV0a5 3~OZ[eVL!#%'w)*x++~+*P)l'%0"\{ b 43\z+nN 7S ; "o#$o%&E&7&%1%M$9#! Lx;J \Ha-^E u 3 u$5;!g"#$%&'$(T(@('*'4& %#A" B\vZ qb n;Y!#&*.T2}579_:L:r975i30o.C,*n)())+,9./G1I22210v.+(%Z" 02!UOeG 6#&(~+- 013:56789:;(%"y =%<krA5 j 4 ;,  &WCx-^5-GU%EG: ) # /S{v|K o&6 ";##:$$#"r!|] ' =}75 N  jxj  h&I BL 8ww]Ccb3>ZPttU1X<vp&@5jW" &cX߭dۥ+د'չ]Ca϶ϚСҀ.{sޮߪ#X $8@0e9iD̾ģ޻lNy黐nѿ]3RǀʃͻsױڼdH]*&pިܨQ0j37Nٵ[:xHț/}+ްw6"VK=Sf4Ldަ^$Փ#ӈCӺw3:Һz{<شڈi6BYQ==DBMH"x OؗՙkҍMDՑdv_{}{c*ch{4wP!]G RG~J&TW8J !"#%&&5'F'&&$%#!;GU 4 D*U5/2!!$')+,b.>01233332;2[10//.Q.).(./.<.&.-~-,+*)C'g%n#!E%(VK8 !"#%;&C''d(i((t'&X% $" !<LQH  2 cos!8a } teY7(RT #!!!\! MTk5  J"d2chI '^|Ufw1Z >cwtB  DysH"*Fp5n/|X;Z  % 6 n @ j h Lep%[{omq~FPS~L?BVmrh8N}u%CYݗ\1ңZ̜&ǸR^Ǻͺyʼ'Oxq n tL=\ T  R -@egq#M? !"#$%i&&J'L'&L&G%#?"a E{6 0R5h{f = *GRvI$4!* !"P"e!zlq 9fg<0 vw,] C.V !m"#######]#@#9#=#Z#p####s#'#""d! 9:9dSf!v"#W%&'()*L++M,,A--s.(/0012'333t3210/#-#+ )&$"m=ռN։ؖZ٥ ,;I<(پo$ؘmxح+Zݾ,y`8*%`Oϰ̭ƟĿnsĮƴȷʑ,nPЎH{ξͽ̦waΔѤc$ׅWkLLpm5yލ#܃U7-% ۥQWEؕ؊J"H۰Z%&AjxjP" wcL WS|g,4bA:(5)m]Od0 IM.T=F+N*Zo }'8#{=1FYGaaL8v#fj*X6Ky-3?c7D U   |  [ S&=}g~fvn 3 av!%U-8MZaaG'bd6cGl=I(qye4Yvt,-1I!#:%2&&&%$" lHLC S y C Z 0 , z U ! f .   =  S 75g/x6Y !!"!X! !J>=|/gkl d"@$6&()+3-|./?0001000Z080&0%0+020,0 0/S/.-,`+)x(&t%$"! Y Z !T"4#"$%%&'N((`))*c***2+++",p,,,,K,++*('r&9%&$A#"9""."t"" #%# #"!P };kb 1MC5  d >SO4UmN L>EN S NgnibXgS41Wm'2ܪٰCp M}Bkؚd֚t5ͩR(VyLQ]|ПѾGY%z_Y C+`+0%"CXXulB/8wBJ)yUKbZBbf-} p +7:k0-=/DNPb>QP{>.[W*X.]X+{c8J$-!>:E{yx{Y_G-%NYK{pX21G62 ݕؘׯg׷W:Xލ/T(2RB+~jOA%ހ܁JՅV΄?gͮμDҙ!44 ف7۩vif߇5>*tI}A nܚeH9& ۗ3:CعؽtIݐGn}}uw7'Ng +-7CW_;V FP 8,So5bR~~!^7 =  9 z b DYR2ha-<7`V1m# 28.9L8nٛ4wֺ{?Բ p]LS3n;RVRE5! ZށMڕ#Ւ*Ѓ,3v2АARetѣc @սvfڀܲ6k{g5kM*PV-7ޗ6'\݆hp/{'MYf| :BN\cyDbk5h8@s\hkV,zK&?WQ A  < @f Q lES/Q2 zvg/&Icx{iB 0 ` N 7 t ]"s #:n$/sG uq4Y 4 i__ ,X8*$aN!1 T c;jW!D }Fuf v!8""#C#H#G#<#;#>#N#^#s#z#h#9#"M"! \ V33`(t;%Fy wpfxrK C!!!!-! , nl|f v6o/   ; zC h  H@\+m!"#$$$$"!H>/_ wheU.HuWc\sv(u e"#$%G&t&p&&%%K$X#V"*!{Q.D i;{<U5w, !"#$%<&&&&<&%$#"!} aA2 G/ X B R@yA%$>lz w N > ~ s x <?4YsmZ+m8!} _vDe>O-J*Vx*`@/=-=[-)ܣ_]یܬܖݭp x-b[z0cy rHkdP~1ީݏܞ_"\%۠ v#iݵ]Y [EtvNv1@IFUY~Pލݻ٫s0Xa YGN(ށ6ilCT!wޠڍه׫zbّ&PH)rߗf"ڦٸ4!ؚإ*=ݠ\:(;s{y^XOmjfSKDDFTkAo>,5U~=`x'Y"W{[  1  : R N =  q i  :qLaj]..h;Cq Z F /  f}2 | * F]7 a"!S.a=MF)= n*{ ? { 2q  CB4*<yY!p!G H a [ B _!wEg#|\. |#h  j G ( 5 j I |rsi\<$o !5"""""d!| nD#z]WJ0"L/:#fu20 !"$$a%%%%&%$#'#I"J!E #h}XCh96\hL Z a   `8@d|1 i f ;&^AqRd91PTU=nM>A]Oz8 p%]Eyr 6!!L"""""i"!  V0,[dVgEtv5-N!??H>[|zxR O &hqf#0vr"k >> Os^Lc@-ZA@UK7ޜlݭK2S U|Qc`!\13flEuܰwhܹ۫ 9YYWUKeKMZ/1vށC//Lmޙ޿ )]ߓi2!` 'N# u a81fo>Jm+5utFߙ<٣؄ذ!z ߤS"*PblthrXZuY0.O[?t I#i(/&`$)-:AA"k,,8{6 KQHi~V=8H\ M v M  : ik)* >~]CGA<":I:r0l  J j uB!  **2UCZ?H yA ?Re?tv 5 t 5 \ ` F ^ $ \ Vk(gRRahv 0nIkc.G$Z x1mAuDN.qlxR2g> " #&$1%9&$''x(((q('&%$#!X 01!8'R   ?HUl7 U2DyP< HqrVEA5#z68;K]_K* j &   ' x k_3t>;(mF! `\ -a q  w ~ 8  6c?my$@F ,} = EQv2 7s T=MV8N. ]#Ecg=G\"r"HFN*`1i^]tqE"?L/&A3y}5ߏas1qުߊPBI<l,u? W1Yb\ߏ :N7f+DP;AM0:t pKQް݈݊ޭ0Y%]DW3bZ~3G_4)e,Z"ycXaP pfWl@ ~Pc 7A'4g_"=Ulxw{hdQJ7$ d{;A0ZbR~UJPx 2TdKCOzY 67 H<wL+c Cy:.UEfhWNAP6e& ( : # /YwnF  m 2 h <&I#?v%&_ p , xppjW:ky@43V*r-?y|;]9MXF2YJBb6w 8!!i""#.##""!V! ^-PO9k] !!!"'""!!*! N k]r !!""""" "J!J &Qf=dcd;kg?]MZGBYuG P   B8H3v1 d  X )f   / 3  d C r , SuH;X%fv ! B@p C w*A?c7WGu^fSNYb+9,zJsR@UIrZ"3h=c8PhZ*~-vp$^( %޶w`,N9lRS9^b. ] ~JS`` ?O]g$ Hv7 l| iEN ]w70F3 j]? *]'z^E*@ys! 9iT1"3{5c 'Qj~lZSA<,ouJT1s G J M \ # " t I90q|0neJ9XO+%M~ -u{$Xo`G#K  Q  oamTL\%X  { 4 '4f?UCVI\ ' 0 q u S < 0 0 : M m 1 z 9 ? 6u01KFC3X-1zf- 5[y#[? Bv!"-$%&'(L(2('0'Z&j%_$Q#E"@!I _&L`vlUJsEraBg#JVE*L76s$f_ e B E El? _  l ( lRj(G y ] 8  R { ]  ?x%PRD f Rw  A x &X) &^Z- <%AK~Gy/TgW/3g}V4 9dHT$mGOy9V$s P a3!5#Zd32R*l8ߎߣ'޵`%8݃ީy[\[hg]ElR:B',& :ކݭܘ%ݓއߚ5#& AI(\ QU2=]$ iy'm_TcF 494&Qn>`S2~\GaJv0,uc+Q> vm@m@ In"Tt%fa>4 brOzE0UQ-FEhTaY76~avXEq N3?ZxEdG{ # m 7 > |&}^DFHb\> q , ^#_JUN4a)vv ' > U y .$jJ>?Rq Q[Y 9xQ <}Y' " v   = > 9BT[|{ $&  o ; U , E~Muzy+<AVTsA&Sl !w"J##K$w$h$#$##t"! !h Mw!aalaReBL?i0|W[NM Ecx a; F>15Df{ l ; 0 I 2 ]  U [DQ"yB;iC:Uvw Ek)xU |zhOvE5Mrlns$n::SeG,2GE:xH{!.'V-&5uy?7L)*w;=Ff2Y'@n.}G݁LlOޥK +Yޅ:ܓp:K3}|5:@'b;f~n&n@ U:XL40f"~E_^i0t{ni~M0.:YSp$XF k 0AF: `,+07TVI( [SIDo n\i'"@({04 Qx_ZK^"68/@JG$>XWUM6& O@d}P}s~p]<h!F  | D  zJ]W `CA V g ` E " &bMeIC   \7m>    6 s `  Jb i aOPkBeKQX!Lt]l.DkW;)7BL !!T"""""S"!!1 Ol[ g+GJ1:~U  H c \ 7  T!mzk[ p , _ S b P0) v@ 6VqrI  v |6"{p`pC`4aV0:;4(C nsl Bq_K(/UY>7,  aXk(uqU%[ed~3aq -qEYCM)|M -Nd1h5o\pb"sevLu99B? d :6 *Ot]\P)$UI$.IHR>6%-XjY1l!~XQ49P<|9nJW qOfY)+k&n\"E"@J?~][=IH8* 1`U0AiPRmmHGUt>~mP_)ML$x!m={'z;Q,cb0@{yZjf,}Qpf ? @ w ~ F    t9 =u iHS C  j|D aX$s * c  F " . j r 4  7P7oDxoTSE w g N :    + w L;k%E5INcq-eL\]n7LP/ n!n b;d*z< A!t!w!>! A $]kp bb}_o+ d 1u$>7]=ec`xq 6vZ@& tU'3UaneU4~+u@?_[&~`w[u9 Ktx VKM Xz5l0D QkB}`W]eO0"_qR(/W'UkcOHA2D2x#Dv6e)lCJ$ $;KRB%n( myU?eTc,pXMGJHH1)x9:e^&0[R>G{(3kY/A'"_IRd&_![W`,o_uw9b&fEQ.'~px EgMob[7*[v@h O/zW*u%A-f=wWCsr-mmEf"m7/gr'vN. f  4 ! Js0#85(jEi$y   & a   [ T yjP; lMo-xJ11Ip*cy=l0xH.  n k v :]Nu00O"Tl{fzAr!Z* ] ; > R DeG7#|!MyDS&5&q:ObWc X)1-Y4)3h>~vPQZ4tV *E@G#$V S '  o W F 2 * ' 0 F h  c +( f P ( ] J RD4S {ninwMK s'^]6aIPs9[ eXjV_a'E`lz W%]|*`,4]n#IbqupfN7n< WUJ0BVf2gCBhLAzEC7qIs3{B 7n?R~+||\5@]sy(#wh zO$ i'Gf/):MR17PVP9Vu{^mP8%%Cz )5$?HhdCFTL@Z !X_:)(<c0UQx^X z ( k w - X E(9l}!s  N @ W joDw ~ XY!AKcWN{9 $d=^-,>bo-vjaTV96I ~ g U ? 5 , 0 > \ 3+hqX`*\(bgW  OCO oMV?Qt5 m"u bN;.1WVY\65Q^+jrPdfZ>* o a  8 o  wqAb,OyX"%|=M$l0DIn7z\YYx%z-Yc7:{`5?_bJ&Io%j/%& h6GE1Zp.G#b \Z(Aj|aS0 ?N7anz8*V%BNRB<,3Aiq6|UwHNLn915>ijPHB [,bdCiz_:q2ViT$ #Jw+k='8J(XY)TX]IzM($U%pl `TU5nzLj)v= 8cD-Hx87"rd K b ;{O--;L]n{"V>  m 7  m J /    z 6 : | F&Hp  0 < I M V _ l ' \  % '  6 > d+_?^)uWA;8Na8 G}b'_d2$5G^~,g*J^fbR8lAxS5 |GX7 2BRblxz~{xwtwvwxyvpcQ:h-^+yN&nV=) '"44?GJTW\_\\WOG5)iD' |ve_OE:-*"(*4ETm$0;FMTY`bilrs}y~wuhaQF8) (0=KRafqy~ (0?FNSXZ\_Y`W^V\V[[Z`\f_kbmhlllqpvv~mmpong-0.9.1/resources/sounds/copyright0000644000175000017500000000047011132614366017246 0ustar andreandreThis copyright/license notice applies to the following files: pong_1.wav pong_2.wav score.wav Copyright: Copyright (C) 2009 Michael Fischer License: The files are licensed under the Creative Commons by-sa 3.0 license. http://creativecommons.org/licenses/by-sa/3.0/legalcode mmpong-0.9.1/resources/images/0000755000175000017500000000000011354734770015254 5ustar andreandremmpong-0.9.1/resources/images/score.png0000644000175000017500000003201011127755075017070 0ustar andreandrePNG  IHDR}W sRGBbKGD pHYs  tIME ;4cDCHtEXtCommentCreated with GIMPW IDATxwU'=$$i D HSQl t/C*RTi PPHH!=}fٙٹ3;{O>{9{HmK1-Gof1Rҳf4z{I:SҺ)zKfMKva3m+t`k3{cH~~Idb$]bf#I'zԹ(n/)oʢjfׅa2 f6?ʹޕ?{i`6~أ X >jK[~~:M/&Gxس!0]ˀa TcaWrn7+h8!z2i7(Ku?p{@ @vÓ<8~nv/#gj!0t+ٙvˡ 벜!LɈK<8BYu`jUh}zg<-fHuyמfU``;?`o`AFtѯ#ɠII3e %YMRU[NoIS$M̨%XHmٌ2=g'{gDאmw; {Jtko,Y3܃JeW$]]r}>o,]3tPTh{VҌxvMnɬ0ph4`[`%`pqF{h -_ͨϸuⵁU{Un'0#2=w.ޣzzPw1to[WW|sJFķ=wNߒݬ5(Fɢڿ>|w x087<_GyR h`-g@qvl\zȀm[w ii2 tЯ"ZZd`gCju|Y]뉇 =u= ] ;Ĝ7E)`2vn@_ল7Q}`5lKV^bI{HzD&Jf+I#+`>!6Iיf6S%UF~C$!t T:RX%ifKм, [.z*ޝsW'})?/IWy5VI:By sJ΃$BRW%m'iQiY)F%0wUH>4 %]oKZ(vxw9约F󴇲=sLva챞/s`h\̆U~?Xڃi/u|;fMbeKڧ2}b+ ֗4(kdH NIYn=|53'tĝ!d:rͯ+fV5#]}Qʄ:3k}@}lft7IK)k%DE^@-Q)dk_G>w<깜 | bf se8GeRA¾4γJ%tQGu6eisv/i zƻG%Qg;eXP]"uu u|S5ҢϬG):׊^l74,˫@:*KY)C<@>G7HfP7l^ ʰ@lM31ڲV'~ԻfG5s+lW#,KJkWv z5`8 { ]]Xt^;͈ i]yXB ':9ɝ&zSM5#qpZ@pd1f^HӥS8|簭$IEq}woh2zdaHqFUP@zNuϴ vZz AfD:^lE wg}/~(+is(~,{cm1@q#lA|efSm!mFO%0/D$JѦz6حi[jXq^% 5N*Uj<n dktVֆON~`+/ ?0"߼؉ #̦6`l\3{3v@`h}^;&fZT~QZ,fVBZ7HI[9|^j6}F-ϡ9E@4glz7J5B-OnWW)x,Ik(|UTxBa= IUICj8/u̞5I9'p'qaFztkYVʑ<> ,p^>m7Sv+²gwWx~p{~'6z({~n\Z܋.,@"`LJ9V]ۯIf٩qGlۈSw IzΫ0:v0 <S u9s~q+{,X|&z;` y3^9GZ| we|n36F[AYYؿ02+S?v|ϴu/`ۍE*eS|J$7YXHhH~Ev'm$ _LB`4ƞd#8#:g6^[PC3C+inAlk.ڰF? ּsuܦY{7vNwǀ['1MY;nc#4^_Ļc2e_apjBzT% 3:AЌR˷alX~[G;c߸̻<.zF/z̡]62g= k=3{wݳ_*!ݳ=ۧ0 x=vZ(o1sHn.=2*:o|S?`gmw\c0NJw#c(${DҾfvNJkg[93uFzJko ǿ[*]c#Cfv{F|:X|JG9^H8o$-Il4IIQV{/3{P.b $}@]Zhבfvct\%zTI)z1I#Gt4v2}%=׹O62HY%횶~$=tlVn3; =߽HY9<ֳۙ\mx ڵaѶeG͖:D#x&ӶWli0@<p{LعG:Q`Tb`2iPw.#drM@U=P~*DdvU[LjYb[NAkIQWk2mQk|)zfӮ5?.+ZEi|s]U(jŎ*]u] ! P8q٩^Xo^I-jK-d*v(ږxQdG tF]7<5mR$㒎p'VwL/gfodμh7&(z5{!RLfJ tS-ʙn& pk`8zm䒕ЁiLh5^ nH?RIᲽ#}O1Li2=wX+юɥ^Dads!g'Ƴ.̈mi<53[vFA\Yf6ݕ%oP*:D1DjJ޼t夘E軾@]9˶ o<̫e}I#"]bfo-#:JѹaLlK]Ea K8*QkWsFd K^TvV<"!вk_#KU# WeXM˓4?@`kcϛټ-' 3`;*S;HjFgj 9k8:(Jiq e6{H:&kNkC-zy'yk):W/BU_ R u!κM83ӆۊ'B#fDm(@嶌$.Lɞĝ劾w3CnPHɈWzNuFes׶NSn@ă\Pށy޹dak'" 1"^!&GڿzEE/I2%^63:.!iu]df].{~Q%ݪwI[uLt QJ7ٵnv׍%}F7:sӥ}838WҮiɔtDI-p֦I0ps뻇Gy6jbIfn:Ҋ(:Q.ӊ'i '`?0,ϰ=̏e~SQ` `G*%=Kܦ.Wg*^oC30 m #]@O? `ӹ)ۃK©lFre;ҳ ̹ ܵfP&'\HLq) 5?xV`]!3;.!i E}%F~H:̾_g}$=,iz&2I'z(B3;[u3#U.HuGu,`v .ǹ3; {2*w[M`x| ʹ`6bcf ؝ɰ_.KsHe o t}gyJg'aKadz g`GxK< V;u|]` ;\`V囻D 2ճ +f\FA19~l>Tm^=z. B)o l@׷l{*NC5:3[v\Fv_);mgg"MQhs{fxxǿFQVQ}$|m 6FF60 8ͨăxLZ圌~ y$CݖP'*6CFLmw;c":|Z|L3Vݸ]uF2]Vd֠n}/#'u1eD;R<Sv;lyVDV\]?^e ڐR.'6.}¨(%S{n~.ԝhJ1 XR–[62H Ϻ{̌=ķ:˗w3=:&R\[Gq!d'vbʌ[2%8v]Om"댖8}gI$S1n,O[{ pmL|. ٝny\F p'O6suж[6m_䉶 6YJճÝYo;:?, \_snq[L%\}ǔS#I 6oR/}4,"4-Jb깥Ʒpnn$D7y+B2t}n$GlJqBW.NgJk0F`S xmrzQh4ByֈzV Iɷz ǓSi fl ̧sbh9c=sq膸t]S"i6%6M :mfFL6Eڒf혤FJ9 8\v<Ҏ@&JKTefogDۋ+fFJ O#WIDATL33[6@QrrL&F/e"z#$=I竃УU =5'َ-nL Iy91q'֓/ٝަ&2[w k'99ɨT5piJ8E>0u=߈9ۜlϫhŚA[#3Xk`DBڂ:~08-ufV¬hEGix;PRe@:Jǃ@Ed}E9Jq{2rEҶ@noti׉z,hsu\gf 8>fa 5V:8+>oӧvO< RrjX(J7݁^#cěmx)Jv׏2- wld 8E O'AhnJy XҌ6Ё|:N[Tz3F>s@3dZHIT=},6f)#i@ 8'.[ZV̟q9P<^o!^7K嗧'z-MLx;ap<[;6޸|ж#7Qv}I}s4'x!b6_4Fѳ@҃J,q=g>u8z{g`#*9Iel;B򻯍uO>>4waw^.tVs*0\Ҫ)띓zί$}ɼ̅63{D;EDy$'i-2_t]Żg$}]))ƫْ5'<ђwa'P3wB^}.(1K>fspU=$\8G.Z19Ogb6c$xO!fN/H ]*dPұfXQ%YWV@ q%8 XHz,vuN.qͳhsWr v}ٽʍ 8XVk}7 =Ǯ-ܳkwkE9ˀ㛠TikWYUo#`L`xrWϖ\:wspiж^۠ n~MvA_/βr.t,yLpˏ1 H~lW į\iw_kp"5QJ7g.MI#n3QnTQƫ@?6uW. Sg*ÛTe"Еvu_?aN/zwpX|{Kϻ'9o'6 PS+yY^]|ruus|[^oIs [,5Cҗ$]剖$mVNY׫IPw0)<VNV~z+#N0[YO$ Ņ}bf@_@Qk1\U|`+*L6H@*N|f[ϔOͯ +Ak0Mע_BOye<0]t ܗ]M\$2 F_:n{5prN-or 6Ii(ӗM|- +ʙ |?vt 5I~fV|sk88W> 2g`H 4<NA 7n\`/#;N?qn#ifWP\̗tJ=ُ#vl\m6fū vw-_|Si]tvv‹fv'9'VIc$ DcKIx̞ko>t *'$]p?@}}IǹgH9撚jSI;K:@e iyT}$L׫p64`*O%ʖB~af Slg3xeK#9;ƓB;u氈otIKY537DŽWWϑcfUxǀc$9rG^c)f)tЈ%YISZ=T~POE_247m?E%;9#T `6[=~t(9Y萤]=3Rї1S]R|ثaH|[K&EuwD]MƾHvLxmz5^rAo|f[ܝٛ-η:YcfkU>UU6do1df?iW#qRoJeNE|Ul3Sla b3iH2+3Z#kM-?YUMG%bILׇ՘ ߂!Ι.x@W滫dqvmqWS;\4 J:U)@;erGĵ&gRKP(Wi\ʻ *a:[5.BgI0E7~t$nN5-N%%|9#nY><3{N]7$X뀡m@g3a ncfHb vm!Ճ>3E&m2cRt7OMhab+8 Wkи_=JK@ٚ,tekA- O«pIiUBH:ttY !m{KVvr"64Ж=mev|,4o*3>Gddwӡrn'< .%_о]YQ3? 5h|P/00Жm.TysYkqz ?*|s{܅)ExչuֻsvS0xu9spYFB`;3}׳ޫ,\Un:oi>p'_Xꢣ+cha1oJ|\ Ͷ%I"5=2n>Ifk s 1 +O@݇g%J| hQ?(ڳ5IENDB`mmpong-0.9.1/resources/images/field.png0000644000175000017500000000720311127755075017046 0ustar andreandrePNG  IHDR@]sRGBbKGD pHYs  tIME $ 8tEXtCommentCreated with GIMPW IDATx]iUřa11VAY4%qAq5eD#Fq)r(A)Sq/ F&JDGYTME5l* 3 k{>ק}}$I~3H3Bo4kI$lI;ݟD$X:kIN%ylљ$!yIArZ;Hv$ǒ|"[m6'kHV MkU\p]VV$o++^CVzOOhEgݝ$c^l='S6/%.\d$qٸ)|:|F؃Hd7cm~$&lPVu$[; yY5)jI=H&yg>L(˪{LNhHVxgUj 9F)|Y,МAOJ^T_.ԝ-'2a;z4QwIܬи¡/vgH~Swm| :5t6(nI4z*̝prG<蜩ob$ uOyJ?uVK|sv"[z-1c68c̫&5$[2σ2 яEJea@!iFPQXW@l𗹎*mYQ<@:6߃Jyt e}J Z eƘt: e[t{R%aWxb #>Xf%\KJVƘ߯ xY)?p(#>cLnJr|R>7-Q0U1ԥ*_,/S4O'_ ec\bڧK_f GS3_XhPk `G)ĩ~/F朿7H' 7a'!NN](GwADg/`M0£d>rqqI`'6hӟGGB~P(şc\O@;|} 0 { UT hSrG `l)`;i ۍ5ƨƘu 9Qo#d,yF X [zb9`.hȗC&4ЃdU _IN$uFc^/9(9IȺDŽ0dy s#/~J BY+Xb oJZkYtnv*6JH>P 2ɮ$)륙jn 1H/1󼖄q]|$ڕ$"DžM$TG +}SԽq(E_>n*̻dbwq<g+ރ讽c s_b?d^i-6#rdv;#c$IENDB`mmpong-0.9.1/resources/icon/0000755000175000017500000000000011354734770014737 5ustar andreandremmpong-0.9.1/resources/icon/mmpong.ico0000644000175000017500000030253611127713510016724 0ustar andreandre (V@@ (B~00 %J  Np h( Ht &&44??FFAA66)){Q4LLTTDymm||$$& DDWW/MM___**<<w ":##55UZKKgg~paa}tthmmRZZz(==aaH 00<aa e  ..m  %%  GGh    !  ==Azz !#$%&'('&&$#!  i "$')*,--.--,*)'%#  77 !$(*-/1234444210.+)&#  ``rg1C  #'*-03579:;;;;:8642/,(%! 5o,wX&  $(,047:<>ABCDDBB@>;852.*&" ADGIJLLLKJHFC@<840,(# >B\zSCC  %).38<@EILOQSTTTTRQNKGC?:62,(# mmg|U(ii $).39>CHLPTWZ\^^^]\YWSOKFA<82-(# .P #(.39>DJOTX]`cfhhhgec`\XSNIC=82,(" e!< v !',28>DJPV\afimprrrroliea[VPJD=82,&  # $*18@HPYbkt}vmd[SJB:3,% 'G{xlV  &,46/(! %Jzx[ G !(/6>GPYcmw{qf]SJA92*$ )QE{bd #*18AJT^gr|vlbXNE<4,& ;t~*k=;vv $+2:DMWakv|pf\RH?6/(! P{-+]ZB RR &,4GQ[fr}ymbWMD;2+$ RR|~zwM oA&3a.^h  (.7?HR]ht|pdZOF<4,% +A|e$g=d  (/7@JT^iu~rg\QG>6.&  =p~|z3y]0!XX !(07@JT_jvth]RH?6.'  b~|zx;vG !(07@JT_jvui^SI@7.(  =@~}{zxx}||6o  (/7@JT^ivui^SJ@7/(  :f~|zywt Ocm+FF '.6?HR]htui^SI@7.(  pqp}{zxvu`d,7sL}@0 &.6>GQ\gr~th]RH?6.'  5A~|vutd_#* TT%,46.&  ~}vsda"$+2:DMWalx{pdYOF=4-& 2>~|rZX' HH")18AJT^htxlaWMD;3,$ rr~}{||&!(.6>GPZdoz~th^TKB:2*# ;T}|zx:\ ""&,46/(" 0?~|zywvvrqE&3|Rʥh  (.5FNU\cjrx~}wpjbZSLD=60)# :@}|zxwuz6%J<{Y $,3:@GNU\binty}}xsnhb[TMF@82,&  8?~|zxwut{]VKe xk6%.4:AHNTZ`fjoruwxxwurnjf`ZTNG@:4.(" 2<~|zywvtswl5/?u8\  05;AGMRX]behklmmlkheb]XRMGA;50*$ 1>~|zzxsttrz@}i-JFqNN05;@FKPTX\_abccba_\XTPKF@;60+&! bbUo~}zyxvrqN8 '5:?CHLPSVXYZZYXVSPLHD?:50,'" 5:~|zvuw/n<vAA08<@DGJLNPPPPOMKHDA=940+'" NOVj~||zztz}] JJ19??@?>=;9640-)&"AD6bx~yxwutp*bMtWS JJ&4788876420-*'$! KL-FT~utn>ovr{D DABZZ"|}'(**'"\\ DF +:b q'zV,A `6~~wtrsE \+|bMv]X &H<xHiz~zM~~|~|rqrN068WRp mB"lxP*tb-Խ%|}~|yppp"Ki8/LSK{&|zs3;CbZ}Q%{zyonm̒Us4~?Qa!E] Ȩ Iy< b@m|xnppd$Q d<m6~oz6zbw x|oti5f(=vDV7D=i{4@i|cyz#tT~"tQ@k0%T` F 7,XBo2tVryfK[rmge{V)j X}b{b}`*[=Qb.g'D-~g5eƪ_@&( ? C5G &C x>%%!OpQx`"pPc%1Izz ]~|`$e,YV!WsW2T"Zc?{U%}km6`R"ͼ4GVNbmuT`?ThYc$@4~\{kJ;H Mʟy?DUafukVx2zFvQ h}FT }\ bs59=K&v}k=Zn8zTpry[XFO[c!4^Om,.*"+Xi14| \!^$n[asLVf}]Rh&$pb pxx@b*u : ]%oOB VQqF ~C b i`;?ntF)̦ Xfļ=RQS@d)2D`MT]kM jcg+ճ>b"ϼMh Urc$_W{Qn&t0[ }m'}a-R,; PwR- mc}DO+n4EPH ]Lj3}fHZArL\Lȇ2DGm>={ `{o1m9 o-`)R S{:N= Fb%D\Vk|Dž\"V`KJ0uRwT/xI0̛@V4W,<: xQ`"Dpe+_~@V_n4 4a"z\#l5h,=g8z7-???????~?g?qp8 c81??CÀ!?||@>~ 8a0g??g(@ .00suu``X((jii6VbbYY$$Z@@DD zz77pRR7D   **kR  uu  %(**)'#  L &,1578740*$ X}~' S  )18?DGHFC>7/' Q|wOO  )3=FNTXYXTME<2(/w.tt (3>JU_flmkf^TI=2' 7 #/N`sucQ@1#POeGvv&4DWjnZH7( ];CWW (8I]rxcN<, n~plŚDZb +:MbxiS@0! }r@{e] ^! ,/! |\hh!/>Pbwq]K:+ {}z}!)7GXj|xfTD4' r{xu3#;/=M\kzxiZJ;."x|yyJ#6{C!4AN[gry}|yqg[M@3' w}zvvrKKeDV\ /~~5ALV_ehhe_VLA6+ w~zv|<N-JKK->FMRUURNG>5," }}yg }!-``-=ACDB>82*" c|x|4Y//l rr&+/.*"j~=_g p 6p}z-;CW'|~{"Z<HRX2f{yqyG)H}>nDWuzvc?,pŰ eRb%]sX7dx٪L@*m[tab pq-Pvp`Nli??PNnT 'cil::6xU<e} f e.ygF7 nE?Fk}/{bN^yB/?Lx/ mGkB@ (B` :Q2Xw^$%}` kQRjژ1bW2zKg>:KFyG[o:ND 8x? k[ψ~*VA6t<:NU(<X%h*t4#l yh7=??????O?_'@ n?I??(0`  % 9""; * WWkii| ``zvv%88?..2JJS!77? UUk  }}   #>>V "'+-+'" irg (18>?>91( p@|* )6BMTVTND8+ #"'6FVdmqnfYJ:* ' !1DWk|p]I6% xU ':PhpXA-^ie[ -B[vfL5" nL<B1HcqU;' {~7?R%dv* 3KhxZ@* wʘ(44O 3Kh{\A+ }{Lqe 1HdxZ@* }sǓ! HHb-B\yqU<( 0\':QkeM6$ |{61EZqnXB/}yz?dW+ &&(!8J\lyyl[I7& ~zvu,dP{\  8HU_dd_UH9+ |xI^NJ TTi,?GLLH@6+  }|oǣ { DDY&,12-$ ufI8pJ~}CAG<tSݰ}Ov8o-}tr{~eV!zu9ӮסԱ,P~DwJG;zU6uʹl2(P܄-LFGLFK*FS Bt&S3l\ Z _Hw6Cn̺I%0yJLCShƤ|j.yΨfazF jp (d^{DlA8{UTp)q,eQ.SsORq}N6R4.Q%Ww-PDvO u'a#pRlT=D= ESUVom G@k]"*e.j@D[?????k?@ ? #( @  ZZHq}lBB<yjj@??!hh=77   mmT #-1/'*557 );IPMB1a.||^&>WlwsbJ1 f 3SteD'};phhS !>d~U2}zD%!$Dn`9 ݟ:7$Doc; {mE jjW >e]8 Tn\3TyuP0zNò "@\vu[>%{{{ֹrSt\ %>R]]R@, |W;JLL5-483&}7ngpԼXS5i]mş4|e!U=RҢns%?[VToÊ`|9uFWETI-{6/tjGfĆN"z"t~k]CUw),ۿ Rs?P}ӸC4cxzll%D< HB 'Kjr #l,=??@{(  +ՒܩY]׵77' ̅ˊ (>9̰!W|Icp œ2~p(Yf I2v+vg\%ZX~C`\ 1uGG* 3paQt|f}ֵtŊ+'FMxֹ%. &_@YŹxθH^^Hׁl D!-?mmpong-0.9.1/resources/icon/mmpong.svg0000644000175000017500000006704111127713510016750 0ustar andreandre image/svg+xml mmpong-0.9.1/resources/icon/mmpong.icns0000644000175000017500000013756611127713510017117 0ustar andreandreicnsvit3296gz7#lh=V_44az :Q`p+_~@ w/xV4W<=Fb\\VKuGm=9-`ӯNO+nڳHj}HZr꪿DW{[};Pw-`M]M҆gbhUc܀ـFCb`ʕtXRQD}Rhpѥxb:]BVc^mi\^asڀ}ƷsknzyXHʀyĂkzvގӁ腂 RGuYc~{J YW۪T{؂}m `pĊѱ~`e GCЋ OxbޑȰҸgƀ@(?C筬ڮ{}`[F7β밲yK[ri4i٨tkTz̍i fޥvٹ=yb ݆Qmҋ6C}s4a]؄tbԯؕKiS{z3i~8m"lPzA |MXHx wDy}]ۄ ݁ m>: NjӭP  Yyĭ ~pNꮲ  uk㳔6 IćA꫿ ĥhxHTH&ʄ 㤕R.ۇPέZ ԄفƚXҲ ʓN ¨tV @iTBZ}\FHn gg J޼LF oφD9сՎDb؀ ܪtJ)SY ˹ޔ\ A'Oj ': n N(boˉN >}-J(<ٝl?u\ ?ʂ  ÑVek! ߀ @Ӄ %䴮{  = } !? ήE|ǰ #\ а˭ R"ƪ " FwT\ Hrۣ&Á>X TaA_FqǓd7s}@fٮm@Ҡ6XۤGpҗy0AŌg,Rワo3a^RÁ ୯{d‘Zv ֱBtಙkQ뺞{JĢG ĢVG Ӯci_z> D ﶲǢpBԮyP殲 Zﱲ<i୯.C ߀ mւȭ| ߀ ߀ BŨz߀ QέX߂oX 7 `rg ߃߁ z߇߃ ߂ = % G  .a   0= aZ mtaKg#Ǥ5 *<Mƚ_DWm|$L休T  &4?FA6)D0@D|J0 `{1R S{: E]\L2&t }m-R, mc}k jc>"M_i?n) f=S@)2$ px@u oOQq4.*" +X4| n[V b9=K &v}ZprFOMDUa fux2 }F"4NbmT?h@4 V!W2Z?% c%1Iz ] & x >%%!pQ=Qg'D-e_eV) X}Xo2tVr@|cyz#tT"@%`  x|ot=VDI m |xnppd d<~zZQ%{zyonmU?QE %|}~|yp" /LK|M~~|~|rqrN06Wp ',` ~~wtrsE \+v]&3h%~~zuuttR6 *6<+b %s||~wvut m c!_  G~~xyxwu ,2f t. joT|zzxv `eG{EP[_~ }}|{zxv I`fU,S~}|y}x34=][9 ~}|{wgz8 ~}|{zx{vpk p}sEfr{1~|{zrlYDAZ|\Dr{9с5ՎA6x”ʐ~yxwutp*bMJ?R4y̗–㿎~|zzxxqt|P! A=NVҰܲ~||zztz}] =5ʹհ~|zvuw/ N\߀ ??@?>=;9640-)&"19FNU\cjrx~}wpjbZSLD=60)#  (.56/(" !'.5=EMV_hr{wnd[RJA92+$ 3#*18@IR[dny~tj`WNE<4.'  &,4GPZdoz~th^TKB:2*# 6 ")18AJT^htýxlaWMD;3,$ 7$+2:DMWalxļ{pdYOF=4-& 7 %,46.&  8 &.6>GQ\gr~Ƽth]RH?6.'  8 '.6?HR]htȾui^SI@7.(  8  (/7@JT^ivui^SJ@7/(  9 !(07@JT_jvui^SI@7.(  9 !(07@JT_jvȾth]RH?6.'  9  (/7@JT^iuƼ~rg\QG>6.&  :  (.7?HR]ht|pdZOF<4,% :  '.6>GQ[fr}ļymbWMD;2+$ : &,4GPYcmw{qf]SJA92*$ 9  &,46/(! 9 $*18@HPYbkt}vmd[SJB:3,%  !(.5DJPV\afimproliea[VPJD=82,&   #(.39>DJOTX]`cfhgec`\XSNIC=82,("  $).39>CHLPTWZ\^]\YWSOKFA<82-(#   %).38<@EILOQSTRQNKGC?:62,(#   %).26:>ADGIJLKJHFC@<840,(# 1  $(,047:<>ABCDDBB@>;852.*&"   #'*-03579:;:8642/,(%!  !$(*-/1234210.+)&#  . "$')*,--.--,*)'%#  , !#$%&'('&&$#!    !              t8mk@8-5,n "\ x"eTܚI, %Vk"`R>{om o)4P L3fALQn0'a RDT+ r$ ~ ;F̪d]&b* % !O,1!$Lf\5=8T[[ ?VFQhTV`T$\k;s"cUk6`"Pz|$,  .~5& 5{jbb* ,Bfmg{~Q0bw5(D7<@$6ob~! -8&s;zzRBx*V 6b ?>91( p|@*V>> "'+-+'" rig 㳳  #kUU  }}ǘ庺 !漼?772..羾SJJ%ϟ䴴?88z``vvkWWʥ|ii % 9 ;""* h8mk "*.=EV Gw-v'T),Ԙ6RQ%ٯ{.a C%yJ 3\ _P-FF*G6l2PD-~ޘA Y̷8i^ (,P{Wb!Oq (Rd* <e'"|*V #k!?2S%?zk| %9;* il32#l,=D<H B' Kj rRs?P}C4czxl%l"t~k׊C]wU,)ۄ /6tGjfڀN"zFW܁EIT-{?[TVo|`u9eU!=ؚRޖҌn%s5i]ߏօm|4pԕXS5LL-蹹483&}n7g %>R]]R@, |;WJʫ"@\vu[>%{{{Srt\3TyuP0ȞzN Wjj >e]8 ܨnT\ά$Doc; Ȥ{mE!$Dn`9 ݏ7:Shh !>d~U2}zD%f 3SteD'}܀;p^||&>WlwsbJ1 Җ755 );IPMB1ہa. #-1/'ͻ*  Tmm =hh77@jj!??y HZZq}l9ߋ' ]77+Ys8mk!?&@^H M. f'tu`Igf']+Ymmpong-0.9.1/resources/icon/mmpong.png0000644000175000017500000010002211127720670016725 0ustar andreandrePNG  IHDR\rfsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwXv,ػ{]FFS5$7i7GcF55j,+vABǻe٥Xܸݙywsf̙3† 'vulذaS6l< 16`cMذcS6l< 16`cMذcS6l< 16`cMذcS6l< 16`cMذcS6l< 16`F1)9ZdJ+J-( Ic\zDɍ0Rua /ք& a0J)0Ml<RQV"5_Y=VBS;0(mDƊ:XQFĵDRE 02@3|RO) A dk_kq) CD&npRo7?w+@dfS?#"2A?=hXJRΪbpxtͳaA `fKƯUjy~"=F)x-j8L,߱?mr-E|ZÜHs]}(Ff8>rk$?A"ai3jHOkA LاTl ? 3hv²0.d*{`QV@b(@)X^Ǩ'U8(gFڍRV|԰"mK! $wEZaFK񏠳HFȀ-ibKb|DUDz}RB%8;lX]cUvR?ZǏuP=3PZ϶u`8\+ş ͱ);D\fV[xLDHS1* p%2#W [AglitnOVo_YĽ~&s/oM<@{a1"ZhoE{}`m($xI;srG&ʂi=y}I!:Hmx+UN]}E?ՆH /UGV*r=uoGe?tPM;su]B`nQ)4 KApo\Hf@V7|:ۍǑڻ ]lWE\‚0$4~jw2j"Z` ɛJ-(T* +> DI}sƎ‰ϠTznxnbN_iv_w@m*}lSz|/fCn+21 >9i1Sb$Wog"҆B[]4,[ߩń}"bq aj9EJS%W~E|."ҙyXˏ}MomfnmTtqeS6rt*AE=cƘi.R) k=inڏ@BM^E]&R,s%̓бorK3nYG|Wz8GC&xG94 cޡv^"-} ~JKi.-P'%\]oW>9ئ6Rߕ3>vO Y-! ^ BV/3vI!fHgY|MsKİxsiJ)]WᛚPyux%n9z 2 {3.2 `-^*CCK?pK3Pqcm`/ce7 2UWDd fxCI;~P}ՑM|5sSJFL#eZ=bH^7^Y򇊌TiN \ [^QG {~"a]`7JZH0VY՛]VZz\^AZZZ:{rG>x<Pv~Ǖ(G<>jE2n1l/Rj@Ht.c,xDǠ[WLD 3e̽dO}Zm'ᓎ0CRҽtz:Ws i0zR?{/ҽ%|YZ^*^$q.8(@xxZAs0`֠GL :2r:Xǖ'.N" wAP'$gaEs?0B=wzXb;tKA^Am>,OԵžcH8 OB ; Ӫ.^68ɵeG$fq >~>E"/wޑ8;BnTHknY(7yp`'\\h Ra ,LмHu^"-wN^#s6 Ϳ`PYY {E."߆k8m8Up[8vzߏ0l#ŦH&F(􎅘avxw܎a >{m`au /=,pNG߆`75AWd7kq8BdB; Ϥš [nB>_M 2j(o ǥ="E|:¬~?.{~- _,7iVcS6 ED0耓[5JTȵ2jʤ}ܼ99},:44!x|p] aj@./)|S `$23°[UW,c:_s8<'2uO$9eoȠ'OWx u8_幇} Iϙg~]e]pf[A׊4c? YM5 V;Bߩ~sNE/' evF=Wu0k1;f,3`?5ugX'o%|`%C+NL0QJ |"0!%zS nAWd^蟮U|8 W?x'ALo.Wje&D^š 54;w.,Pv):?z7_m1y̚ϮU/XV= !u=} V~˦)j5) *AδyiV aR"U+=`72~)f"5F^ZBd;ǬU/Y0X^" M6ܾ^2P?5ՔE[; t_f\=@+[б P+okW۷m~m?o 4= -:΍pj6dZPv^sQǐаYϷ'f/ǎFLoUJ u!t8k +ҹkP?7/5~yJEAF/0Ii/;w!f %s4l:9S4?>!@L,,kdIU'j[é8ͻ_Dd<=XV*:06@H0.>5';q{ fHް76ހʕr  %sXڕ%3Ate(_.UKHbeٓܺCrz|7Iwi VV*qPim 7`p&(v"[jш%jUǢ=ߒ$}dѮxF6&8VB5{a줕Y#քXY.ͺeJ_?J1E~K &-l\aR,Srl#-j'R+˵ĿhZ57o,p??{Ltޫ˂0ol`yI]NUхH0e#.5aS1D:uy' T}º-w)_n#*RD:w]3˽tio(3 z .~ =p+eΟz͚c#gub62\dRw}IJ:1Cscp4+"zKuLyv8GEj@BCuKp}#tsaE/ gMvX\ x݁Vi{ Gc۝KLZԏo'R<_ kaEö 1Rdf7x36N$&'/sp(Y {zĮCH  ;Pw `Əքϐ8*̒)6U+^t- 1c*ؽo[xt}gx诇m1VÞ]G+=/b8_ y鰠 CʎNV p}ak9\ l[%ƗքV4D-OW^Y`X>j!]W /=K9SDMR2eN))DŽ"Ot^p})և7MT34LCסE܀$gvgWi< 0D*U6++ڵ(2"0 S! 6'Sx *RCE܀o`GR)9E h%l=< , j}3zty_{7(a[jVٰe+-R<qՁF)@?]x3_{@ɣĦqOQu3 <@AfNm*ty*V3Z{… LRKBiyH;`/>QtKjzf-3J-a0&:AZ[h>}Rn-x;ˈ 3'(30ۣ$> ҡr_f.lz!iAw`P'ҾO_~ad ttl >Ap?f[ ՗EG;`v<BW *;PV0e-6eD}o/Aovdx.с\8[p#-8z%3awva}-\C,6 VhܭBڔkJmB؊]BQ1T03⾂‘y aYXR<"u/7!5ũv{Yb}&s'<M 3?lo@IyCp6sbG:@3f^ %aqZE*ta0P?%J-TH6ehqˠ6eYlt?}7E~;c4P Nbg@oG';)W.<}R;~lO2 J{mTdJi +\_U{iLJ! [n䛡Я"8 Z~)YDa,|sƚރGWk]aȔ(.å (,@1vpb|= *㩀e>Vи5(X_pï\xE~q_B126뿁 _)BWv-OP;+x M2>skGc{J;yt)ٳT E$9 IЬ}݂+AgpO[5Rpj/܃y{k@wjX>*݆C02mP}HAԯK\'2p*\sK>{WÈ3s(<9_C<<ɶGGAySp BL@oC\U` e5j } OAHLga)"ЦK;w\w *WKīϟI-'Np o/c^+Tp8 /MOѺȒJ'A۝a_X(_z~jDPJZYdTStfKuW@e(S Byh$@n _ tGB T U,"|/Z~q/got{J;u8Og}ڸ`|wi~֖2j+aaU9qHnTC jl2mXW:+3ՇǥӐ.P }v/BU+:poLyD\ppqf@u(k9'm?q̥;y9cK"07L,C횣qp<_“JiDDf坙Cx&|;pSEQl6q _ pYP!^>e_< >7j7wjq \#翬l\w۔Tu|S i5=ɩn%\9oCa*h9f j)Ñ% 6SLx_EÌ4( :Z^B|3|8OZRx:cz-ap2adRmm""2“#d֌< x_Şß"w! *%@W *;sbPp]AGE](pAPᏉ!n@d9xՠN|#ֹI#ܗ|`)zrcao8arT1YMNey .Uۜѝ%苧صރ#Q, 62b̤^RsZO{/cXlmG۾:5W6R!u>zH@?~jg# $T2< IDATvpO)P:V ?\`VO+Y(oWm}}WAe[kn%*2e$^ߐРqț$8ژ> }sCkv,C7yocHp<6RUjqf*?vj!Ds-^ۜl6dS LfleM:ftZFK PBd] 3A ) Z_sxGJT)7rWTS(]Jy. p  KHp C @.=Oq :zOhS\9HLz7JCݮpr^bD,hlI?xz;fe s=HcT煺poݛa\A2wޘFiݛaǷ<ȝJ1kț%Rca3ɌXއ wi楡{ ~,i[4o~JK\.$łwF J  ĉ?z,H~U5,TzCzO؍ r|g8 :{%kƎwPP?#Iͼ<;o\Aq 5;'p:.d)OH`f*_狿qКˏCnA9aU Zp"q:H3PhHɆ; (OkS&Lo]ϟQ` B<; k6s.Vh'Wjsƍsq߻[0i7`,dZ` M@6@ ?CHR,凊֔ak ޟ&o;'Lu֌r7 rP%1 6OB^3^,!V,?5~;@v~go2 @~,jηl,}R:RRQ-¦9sA{:.")I^Ӄa٨Աy&-R,^srbK6xw_'J);:PxH8 L Ng sH"4.N(lȠf;}Rl}:oZ\<ܻPhC8).R!8TKgI;Pp>xb`G!V!ۉl%kZpI0Iu'Ci}ײvC:O: 3/:V j=\:ODDä0x=΄Ɩ<^/?xGnqnýR]'__H(kb?oV _NBդ_d,tNfA6pgA'"ácnٛ<<+ Tq07gݤ=PMF?7vu:JF1V-Lq St2m74֖E.SAM0k?HlSH`f(փ3- Ӂ3~0h16r3YdjALo߹}ecP=?* |XjI;.f k'A]}-f8CE*'&C10*,Sc2GE; Ky ˜2(==&Ӽ3Q%\wU+l*,@a@1h,R=;#aXDeGUa.(ozFf40ڔWB-{-2!JZp= q \= ;#BBy.΁O( -w,:NŚW|:`Pr1?mcwY;>݀niu{O`^s}ˊ룘5c0O^DL8p("b? {\Up̍ԗ>%I|ְ֣@_Z֤Y6,x֝ j~ .dQѤ:P&!(NxH= ]#:2 gy"i͡Dг?nADބ{I-!`ryN@َ{f`lu +TjNވ3,ْF7g"\ [aX-aB[6MCh+@;MCȬ$lhv C>Ho~qn}_/ip0ɏfjW8wB4U,)~" 2za#DIL;GLnR36ն@ᾥW:!̩ˆ#5Gaf1bPiKh?fMX1cy{|0-sCLJ@p"6BM,!("ͻÂ*)WLgG*OL"AqX, `7>yaSRaby2@dY7 CpA~a0XXClU5 F"s+j[cy`VE I5rK}UYY_22a B@&m&@oW( [}sxڕ.[KނҿB4Wjlm `~"7@5Zz93:囊Tߜ=, {xs'_RǝA Oˀcl:!A(+eCWAwjZT{-(RxL`xS-\ ~R i'3c8hdt6\ɐ3LZb >06`Y6cI|ga뫤H%1bs]uڛzm;}jؚ ƿP_Yu,b$"T1-M!,ǕK}-m/£m/).{qd)&>llSBE dVd\Zw̛7 39O$O+x ZoECx7p[貵AgIM3Z<-ՙS<4 I sbf9(h@DQY?H$ mp[p=bNj0AYu~d*2=JC;uۼ ]W'P}zk88d <2ƍ$9NQ.XLX-,/H!-`ZT)p+ov~3@nXs68&Qs{P4Pk7%H3{TئFi&^Ȩ*UÆP^nZ|hvnzy¿o)ϹM0}x7/.I' gjlmh?ufȄǠ{Os\kŒտ(Fu[ZM)5ہi]b el-TYP8=$!)pyfA}H c:$|+ܤ?hRS<>bJ%Fʀg+d^hY:H:U-t#p3̗4/.E)qGq0 H=dU v+ RC};HT %u @ 9RbN^]z김W$oO[ < `Z_> F'!g=&T$u ?AYN-0$ǷHۆ0i:!< f-\>e,$x!xMdHʆ_09uvLʱh;͘ۦw>gw+']欆qJ=Vi*1;ݺQ=zx5mꀋK Qì=?'T8qBaC6eO4-0?I=fIX$B:pyXVk:ob6d;`a^_iJCn~]bM T Nbϗ2!;!ĸDw6$@ mΆl(mmΆZPDK״i`٩l bIdƵN4oS`7R݋G`3윪7o^0p`i0'sLׂ Vbޏ[RJM?Se`IeeسGn[t8)-vP.gIN\uHܠt}pt=d Yv[̄2!,T }`rx;Ȇ땠f[@j*Do91nLy29RBdW RN P܁GB%LϟxhC!<ϣTBAy˜N| giL=Շs?D.t`?]zvFa3z;, 9nJt4ْЛ)7sY< fB3|8s |;@qِZ|kje.@e -4+TP2KC^8 V iavHtUWo 62@Dž8 1/t6Tj1(}*:;+J?+d%AprV6=dnlO:ͻR ZR~M @DZ_1cj/^> \a?W*7{} B0,b]XPGfF6c=\vp*zPּǡFf atAn3C=Jb~+N)U9ʛ;)_}0R;u\]޲Z2Js^:_"&8!zp0*K3PM!!YpzA.X;XѸzNк/b ;'s(zrp[ÆUVG{" 2C jwU?kfs4H H A*ȕk)WA)?EA@U"BRHH=9=̚5kާy晲=- 3mS/|>.~3 5U@4XZ.Twk5,ofX9 PqE˜WaSdo'KL+G AﲡCr?xe<0ɟp!e(dOf8ϓ!Y:^{-Ñ~E8x bl Kރgykִ~/s?R[gSTkO*aMw,5V|,vAP}WM[| -j>+C4>0r`e'J~7 %վ;m}=.jA*eݏ,D@ kLoC{<Ho/w/@m͹n)`Ѣ W]@G&̛ CjgCݰ#wZsUB۾P mVfɶR5 mîNau[ZBAy x ]Vvae t;Zi1Tڐ v,vym*w{ {i'm, =bU7ƻbݰG,=ڷ',GC@(j9 GsIIm$1ɯܞ@\/|jY\ YۖBY7]m۶*Bl;۠]m,gs +vHϠ2ՖBBI8*HާqLKa>B IDATЩaRo 1VEDtoX̛ ?eU)xWU.}߻uĈwub[|m A"|E |amiCQ(]%I"ɯ$׿<#%O߬B3[/Uݦj٤ײJc-YagG(ËV@EYdMUCR/ihHu@M'绁RÆndzOC@y.*;mI*%vХ#5 o+wY} t_ӥŽvt uMr;7 p{@H|U(+-< P5 A`hg=C4j9c6uNj^cJ~uKm/UgY nP>Ž١v  zVg`A z75;!0ɿu 9iFΔrE!^.W3E~hbB\3}w[¬N6 j$Áh@QP¯!&A =y" ^( "έFGzQ#"vxA8n |!.Io4H&=@nv> $ɯA_ I^xNސmn,}zZvBi[M X sʒu}[PVc+M7aۡpP~ x.x>h1 HOH93i}r2ܚ/u/ '4!}"G<$|*~nSf/I#됩fRPohBƎ.a}_@ۓ7mU_*]>.:k@-񭯂5}]w GpCv9%;`u ?e_&6DxM|)+][^w1q ?NA7wRe}oD k=w_"k7=AD ·ծS@TM򛞀&w!-!'yfuKrI~= BqEN[zl_ } o :!Gҹp"/KC֐y&;Z'8pz #K7 d B6lkZ`XWld8q)o._q 90l/ cn_ڕhI@!pOk]OZ&r&J,ҧjLB<S "XV fX~^uTOT&e75[%D`!{g? W^ ]g {>O+jHCA,,]UoCW3AP?+87P y: @x*`[D`u^ a{ᚗ|ɵw}x ŜWQ;S!3~'IG>uV'qm-=wݦ?^Z}Q6]m2!hKeu~?ߨUڅ/y6Ϟ̪@]E褷PB5&nh՝وF-e/ׯ ZAa*hʎS0vG!`6u5 +;Cn6D(=鞫q,_>| \o[^v:#~1WMPU&i)G֮!DT1ӡ쮻f}6sٕs +"+o_W;߰ho Br9 kϘ svka-lb}#ý[O"ן[?Zb4 T-|WznZjnw\-xą@R0"oZ-((r  B=Q@?Q >ĭfՎ^JXP -B#R ao kd}&8.' ;T_wx-Xv= Vvaqtp+w:=Xw'j/OÏ΁M !*//:!Cl})ITͤ^b8ȯݞ@R"DR0c) ɿL &͹NVg_41 x*A:uB0;ca&[h[AyaFغwU>%z}KBW5+v%%]YB2D \Bjt`˰h;d| `ʻn!'վ$MxwM7gȐgI6qu{!]!Kr%ͷ~5'r=/3-.oY}[€m>7m/sX~Tl]y|Vʛj_Ek/-_Do6W8`fl=%}YcUGՔRWIhX/$5)4qp*9xhK`"(A]ڪ/O*< ,$%d`uwx};u+ToJhN<:a*_UuKt!R)qAKoT䎾f/i-4SD+dh(8휈[}ЄU^9 5A<_&Ϟa~il.Uî꓂M`G0D i)t=vO~.fko./ ?B}>hNAo<VU*oEWFRomTtȑRmۺ{n4lJ"zIӈR?F"F}z_" "%@p_TO?L21z.r9.;a~06=u& sW|nڟIaP^s`O@<! ]}u8∨O\uq\yvXwxZpn[W P1 n=\s 9>x\ie)2kN> >MAS ,\[gA&g]}@MhAgGIakp[YYi[n)<uƫ" CwBOom5 ˸d!df!Lo?k2w <4#U}u~ަM)^(٢$!odaX.%% 6u˯]}dLVzVOc٢4};=~V^^]ziӰ_xrڄ9b|.l` d?>B1?g%6d#ppi@p@ 6W†_:G8.<AEpʕ3AS f̀ |IGsZ(X=kRcGPCb]ӨmQ҃#Mڈk&5?. a'v6 u(Ȟ/`[ᬳ๷,_e Á ?mG `c @*n4BҘA3_9e<:F&󷯢6OJʮB'Ң݋m[Xhдv?0˅>Y \Ǯp=ϚB61?~\r6,=f353DZCsz:n9>~VMcټj \X| ݣd7]V@~"AၫZ_%~!q&||{nY &́nx.}LuYp'`+{*?R6B< e@].~ۋ/6XWu'ɳ( D d8mq /qχ]`trq;LJ^@t 1nz]ʷb(| 䇂=.;>RꕣǸŎr.i{.n8-=}ၯBy)羽(P73&rʎ4,RChhSyx~#q YQɗ^ًW<.OAaܱw&`m(UOV.f߆ASX.gqANY򻈋c}Ǭph:{:̸ڭQ= s%7CCUwb w)٫x᧾e_w r_= =g!pWȶpB‰][$aCt9ݩve>P5 C^!6m>'zyKkwyRA^xg!nFem4|*grC:/˽]D]=.믃g>Q{IBM ^&%:,Z&&͂?.+¾ -8_mLbP}ku5}qzܿBWbn'd[|ejWmlowW߀~΀O@tۮ4,*ApX3o.L;d>`=s˦zmZlljR ;5ѥj8j^%"KH0h/O]0MX?-KwTxp {7 ~t*`4崭d|&ncfl--cc~5=TUCUlo`^8HN0q4, k>f Ͻc|)gzW]uevI/gڐ>"ϑKy $ 'B뮷u \7qvN;fWnoHU{P &!W-bhz)w&@1H3`ns$!Mڻ3sp AAdJ: 70tj7n֛[v{6Uڶs(4>Q:tP?>Ke> ] B}{rP)j1N[-Bw ]ߟ$>G`=$`oHm k<pۙƗ[Rp4 aFt >zL` vRSH9w &v]I.}$&C( e!K &|4Ak\D@{5h(8c86QO'0 m`l( ]}XP_<J ;w`D>S7_H; Ѳ\1v}'qvM!~Akz!'+7;޷0~3n |5I3dļ*cz\^Vq]0 X҇}`p-ƏE/(w< 0i.!D`pks?O)H!Dg!D7%P 4-S<Ϟɰ6)BK4x$$% `@#g(ƭTdz5(uda#"gǭ?]O @ Rf0-^NG`ZIvE.1/ y&+lkIDAT[aUAlݷ`1t: Ch1(:C`@ù^"qZ,xm)gnM$ @v;v$Z5Lo.6bےڻ>onA;n.4J  _ͮ\ª]PO0Vв# =Q囶KwB Mc2<\qܳHIhUpvQZh65ݴ!92H$y&ٓzu<@z!2A!fXIG 1p o{P ZshD`VyudKlQk2C %T.Bкu1E{"h>i4Yvx^X5uȐ$IǙB ATG& \ֿ0ޛ!4$VY.`H8GοxL}Dh`0r_Jos!_$57 y P@H &_A2h߸'iߤMz loDY~e+*r7/ TkϪbcᱷ%Rnz _i(뮒fI^hɯ5מ@*ɫI$p'zշ{&L&~ɡA»1*J-UmIP^whlMo@MciR~:FT8% ߃NJ=j(/Olծ=̲5]@oѪTNG?G&q@ށH9N!N,~boi8'|M_{`]2Nɮcz:W!({d2h@*G&GE2xo 3!%`}ntvBz"<:u#Cd%{>~? U^ߘb}㯈mب`KJ/k&D'Jپ%&W>wGq!pv> pu2.o.M؜Mm~ul&UiKpv}-\(*? ƴJP $2$rKtS܆>I% \0'yJжMXP͏?aa(v p)̞;^B!Ax30Cdσ $Y^g5C_6h1PņI^YsR7=0UM̛vpÝs(8404\JE@`KɩɯȠvrya?ǹh}t# )=K.uдD C3!/w_]v[Ƽyл[>„7aЪ:6+&I9aRnͤ6loI.\i/ $&`1 Z.u=za?3ot9Mf_aݙǴus'"{(0zl+`72sS`?O&k*{(C)*EСKɄUBB`z7M {ي):j=Z]=#WAhMmDE p3s=j]x.HM_77qJα~z'tJv=|~)c@)ad2q/@/G?Ta{!Co!0 }z&pDdQ p8u a@|2-|Q+Cz 0c2, -'|

s@A^; h~H#J~{%v|eWr0*x]bw”n&l l`' 4Cyo"]R"6L@ /TTs`3fPxk"Idž }!5s'r]ޔ2EYG*q~&llc9;M?rAӕB G&Ø1c/!uP ҡ[dW^ h0;1P 4uݿ'3=!rR&⮿0~<(ו9 :o*صޛ c \t%+YW] :wV>5Aަ1mjYf]\M|uMtaCo‚<(] G>(/@>5F picΛa T-Wiv^ۮ>xN)T7|0 Dvc#2ojpԑO>fruFW/Q+~͝/_DUWzOu5Ss>IfgU f~ ?!t!w?'逇E4FR?Nzg{W:$45*7[ BO6$ %Sp5F5x|KS"JRE?{%j6"-z8?Mr$$v]oT3^š:ÿ|GͰaj {>a20?0gHÊ8RzٺzMEإh$?o5'E4&kvT/Z3;aR0L B͕TS[Xue ["n I|l٥mf 5b`@hQM޿Q0t(|N3GAm^A@nH T2,b&8\@'u^y+$Ó<䳟EtJIq`tɿo`@qOY.]ץK=ʢ^k+o{z̆=fHr227`nz* \a@smSH¯[8 e4}=-֮Y]M?Z'ja]ve7VҴ;D-oZЕ7^?^n{:'O in/C<!\|9~lO^Z ep^r/zOdin[{Gf{.=[I%jѰKdyΝv&ƻ@3yƶV!R:ٰ'Zx]i^v;R^S'44wɿoޒ~x} p o6MmܶQ o[\9~A'@fLVe2Rl2MX0|8Gv{.p_'y.B?.a=v .jhu_^O#?We)k\" u!ĥ/' 7nZ~'.cm B^5`z7ׄ{~)߰%SYɛ?}] '$柳$6M0 w[?i^@C.-~t5vc%=oi+f}4RLڕC?6F4 ̰\V߶D@uqud`.1-IY׿:&)QoB}><ZZ!c DEL'Y}=;%ȸܕpY\ķɿ'gGR8E4D}7\w]a"`e]߮R!-fU`.G=dVW<̮]2N} &B|.^|xX6 Q^@.DvtC$_NsrKsCC @ n0cΝ ?&7Io@8'[ k\vC4!}&r FᗕnHrryI^K 6upRf.N̈́F!ė /ED}߹ Wmn%=W^ R.O͇F!ķ'P9\@n/*1߻IkpEH)y=8F!]]1B_@op g2Rj2ѼhtBGkx9tLE4?D ; x-Z4e@8GM0UJyoAܤI_~cAW/Dy5bAEG&~tZ\"@4!JӦ y_l"X 52'?R1AEhiRʙ4GnԳφ[nQ+x 2̴iT_D.4[T)LSnyi+V4Wi~Ӫi֭Ld,ws= !ėR)iӆ6O>9] ׿Nf<)UO`BJ9*f-L9\אEo ?A>RE)EQ(Rd8/2#GsI??7Nt!EQ !D)SA@ .@r 㚻dMkaH܉˛lE@C &t.#Fz+i5w˗zz pras}{h!ʀRܔN\y%%@N]!Q' !N)e^^'Bՙ R)8l2yA]!k.LHd6nOXN7>)._.Z0!8V*4iCK%8tСKEUرdxx ?=B .MtB !8,S KƍtOWuovyU I)厦-Y;90!8.}3i>C ; @4j;wn8͙9`v|!H>ӌK)wN(nاCKJ8,OP~GI۴ -e߇m`輲` jVȒV|*%3$)yE~#.dE0 AFR*h%%CvۀL |G)bO~-Ecl \DE4 PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1PD1?i3fmGIENDB`mmpong-0.9.1/resources/icons/0000755000175000017500000000000011354734770015122 5ustar andreandremmpong-0.9.1/resources/icons/hicolor/0000755000175000017500000000000011354734770016561 5ustar andreandremmpong-0.9.1/resources/icons/hicolor/48x48/0000755000175000017500000000000011354734770017360 5ustar andreandremmpong-0.9.1/resources/icons/hicolor/48x48/apps/0000755000175000017500000000000011354734770020323 5ustar andreandremmpong-0.9.1/resources/icons/hicolor/48x48/apps/mmpong-gl.png0000644000175000017500000000624711132701062022715 0ustar andreandrePNG  IHDR00WsRGBbKGD pHYs B(xtIME &$lj 'IDATh{U}?{=޹s08ȀJ50ZIKWWM&i%dYyXlm%]MH}5 A,F8 3sw8fP"mcgϦJϤꃺ +,:Y {;1ƧJ`$XzF yޣ@{ j4 }+DWi8.w+w4_|krN"j2=j!BaU&hdᜢXxō<*/˓1Xc|v}lSnJmon'\ΦIaA Rb7H: E9 !H1Ž xwo;j"(Y 6|Bgm?*q.z+FM`X 9 q C.ddp9̂I`CKiIGҙ:;U <#@)^\`^_V" cta   p]ր 0`3D$*G\& TϪ=W`UO]d. TN) HA@뾆pZA%@xU걄6[p`OBgV\}uż3KTZYeFKOuIt ;UEXw ew - S !@@%8-kSۯbԩ嵎 *ͅAOOY.oTD@$dmꠤ9 8c=@lew==gtF|g3WO+..4cfqg >2u9;Jq-`x'{$Gt1c-/5OyR!cE?QksVD ++ʰAJ !ZkCk>J!5;aX0;@i4} 4 3p5lGUs)xh*+!>Xʴi UW U!e9B D )LDPG)\vCJ MWhH2DS !#|gwNbѢ-hvST!DPQFa(y|?)=Lӧ^iNPJDv/@ȰIvAYYZnabN.ډD6kVx 70L3m#D(  #D .0%Z h ́7!Vxu9/(;Jj@HzyAiB,X`};1$ # $J- efa=Z %]{°tl|^ f4[٪_w{Ʒ&3N5DRaVJV>$ CM0 c D `0*/CrE{=zmV!3M&9r~4ڿ|9J>@D ֺO#~N<N}V=Y6KeT& z|? 4[Gpۑ~D@#fdbR% t/׏mw!:ϾBеBY~ ^_:Rh@Z'qA  #BJ}yo(hJ@v48#!H rmh9[Dq xhw\RRcHcnBl B(G $;Bw3d2]xqy #)bC!_LG$W.Pe A*Vym:i|"rf)M, u{Q* d2axXV*rκX/Ӿ!L$mzRY0B!:Ⱦ~H!Dp,>Y>;waAh KYۺ;>αTk+mX@)~!śRYi`Y,Ŷ?0Q[3M4=Y u{ /-8pGkB +ò\B!QX}7d|󧧨(? )\3d y׵} -6}P![M>VX'ij"7}:c;:s/$Rx>> ,0RHD$0Rf ?xx2t)F.:Н>zGt{ Gme^i=hkRu^ bڵT@$:O> S)5e1=ild⫯rx оߔpٳSyU[ˈe|@O^z)SʟZ%tm,~U\}y3{9W,H7r̙ohzbxpJ$ S image/svg+xml mmpong-0.9.1/resources/applications/0000755000175000017500000000000011354734770016475 5ustar andreandremmpong-0.9.1/resources/applications/mmpong-gl.desktop0000644000175000017500000000024111132701610021737 0ustar andreandre[Desktop Entry] Encoding=UTF-8 Type=Application Exec=mmpong-gl Icon=mmpong-gl Categories=Game;ArcadeGame Name=mmpong-gl Comment=massively multiplayer pong game mmpong-0.9.1/lib/0000755000175000017500000000000011354734767012551 5ustar andreandremmpong-0.9.1/lib/game.h0000644000175000017500000001256511131700201013605 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __GAME_HEADER__ #define __GAME_HEADER__ #include #include #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif #define PONG_SUCCESS 0 #define PONG_PADHIT 1 #define PONG_SCORE 2 #define PONG_ARGINVALID (-1) #define PONG_UNSUPPORTED (-2) #define PONG_INTERROR (-3) #define PONG_RANGE_SPREAD 32767 #define GAME_NUM_EPS 1.e-6 // Keep in mind: net traffic is of the essence and, to that end, messages are capped at 256 bytes at the moment #define GAME_MAX_OBJECTS 4 #define GAME_MAX_TEAMS 2 #define GAMEBALL_DIR_MAX 10.0f struct netmessage; // forward declaration // Everything is scaled to [ 0 ; 32767 ] representing float values in [ 0 ; 1 ] // (Mainly due to the fact that transmitting float values over a network // independent of the architectures involved is rather non-trivial) enum gamestatus { gamestatus_running= 'r', gamestatus_onhold= 'h', gamestatus_stall= 's' }; enum gamemode { gamemode_linear= 0, gamemode_badminton }; enum gamepadprofile { gamepadprofile_flat= 0, gamepadprofile_round }; enum gameobjecttype { gameobjecttype_endmarker= 0, gameobjecttype_ball, gameobjecttype_paddle, gameobjecttype_wall // gameobjecttype_blackhole, ... }; // no padding for structures related to the net code #pragma pack(push, 1) struct gameball_public { uint16_t pos[2]; // position uint16_t dir[2]; // direction & velocity in cartesian coordinates // future additions: spin & diameter }; struct gamepaddle_public { uint16_t mean; // position mean uint16_t var; // variance uint16_t size; // racket size // uint16_t dir; // velocity (obviously not an exact science in this context) }; struct gamepaddle_attributes_public { // represents the team uint32_t score; // bind the score to the team uint32_t peers; // players in the team uint16_t profile; // paddle profile }; struct gametime_public { uint32_t tv_sec; uint32_t tv_usec; }; struct gameobject_public { uint16_t type; uint16_t dynlen; // determines to what extend parts of the object will be transmitted during game state updates union { struct gameball_public ball; struct { struct gamepaddle_public pad; struct gamepaddle_attributes_public pad_attr; } paddle; struct { // normals are computable and don't need to be sent over the net uint16_t start[2]; uint16_t end[2]; // possible additions: linear, curved, ... modes } wall; } desc; }; // future additions: // * set a `racket profile` independent of the game model (possibly for each team independently) // * allow `wall profiles` for each wall // * variable number of teams (0 - 4, since we use a square model; possibly up to six once we go cubic) struct gameplay_public { uint16_t version; // gameplay version ( == sizeof(struct gameplay)) struct gametime_public stamp; // time stamp uint16_t mode; uint16_t status; struct gameball_public ball; struct gamepaddle_public pad[GAME_MAX_TEAMS]; struct gamepaddle_attributes_public pad_attr[GAME_MAX_TEAMS]; // struct gameobject_public geo[GAME_MAX_OBJECTS]; // extend for more elaborate game models }; #pragma pack(pop) struct gameball { float pos[2]; float dir[2]; }; struct gamepaddle { float mean; float var; float size; // float dir; }; struct gamepaddle_attributes { // represents the team unsigned score; // bind the score to the team unsigned peers; // players in the team enum gamepadprofile profile; // paddle profile }; struct gameobject { enum gameobjecttype type; short dynlen; // the following are to be embedded sooner or later in a union like the one seen in `struct gameobject` float normal[GAME_MAX_OBJECTS][2]; // normals float start[GAME_MAX_OBJECTS][2]; // start points float end[GAME_MAX_OBJECTS][2]; // end points }; struct gameplay { uint16_t version; struct timeval stamp; enum gamemode mode; enum gamestatus status; struct gameball ball; struct gamepaddle pad[GAME_MAX_TEAMS]; struct gamepaddle_attributes pad_attr[GAME_MAX_TEAMS]; struct timeval lasthit; // stalled state detection struct gameobject geo; }; EXPORT short gameplay_getversion(void); EXPORT struct gameplay *gameplay_create(void); EXPORT int gameplay_init(struct gameplay *, int, const enum gamemode, const enum gamepadprofile); EXPORT int gameplay_update(struct gameplay *, const struct timeval *); EXPORT int gameplay_public_to_internal(const struct gameplay_public *, struct gameplay *); EXPORT int gameplay_internal_to_public(const struct gameplay *, struct gameplay_public *); EXPORT const char *gameplay_spell(const enum gamemode, const enum gamepadprofile); EXPORT int gameplay_parse(const char *, enum gamemode *, enum gamepadprofile *); EXPORT int gameplay_apply_state(const struct netmessage *, struct gameplay *, uint16_t *); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/message.h0000644000175000017500000000526211130511023014315 0ustar andreandre/* Copyright (C) 2008 Kai Hertel, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __MESSAGE_HEADER__ #define __MESSAGE_HEADER__ #include #include #include "game.h" #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif #define NETMESSAGE_MAXLEN 256 #define NETMSG_SUCCESS 0 #define NETMSG_ARGINVALID (-1) #define NETMSG_PARTIAL (-2) #define NETMSG_END_SOCKET (-3) #define NETMSG_FAIL_DELIVER (-4) #define NETMSG_FAIL_SOCKET (-5) #define NETMSG_FAIL_CONVERT (-6) #define NETMSG_FAIL_CHECKSUM (-7) #define NETMSG_FAIL_SYSCRITICAL (-8) enum netmessage_type { NETMSG_STAT= 's', // full game state NETMSG_UPDT= 'u', // game state update NETMSG_EXIT= 'e', // peer exits voluntarily NETMSG_KICK= 'k', // client session terminates NETMSG_POS= 'p' // position update }; // no padding for structures related to the net code #pragma pack(push, 1) struct netmessage_header { uint16_t id; uint16_t len; struct gametime_public stamp; }; struct netmessage { struct netmessage_header hdr; union { char data[NETMESSAGE_MAXLEN -sizeof(struct netmessage_header)]; struct game_state_full { uint16_t team; struct gameplay_public game; } full; struct game_state_part { // this one might need manual adjustments to stay in sync with `struct gameplay_public` struct gametime_public stamp; // time stamp struct gameball_public ball; struct gamepaddle_public pad[2]; } part; uint16_t position; } payload; }; #pragma pack(pop) struct netmessage_buffer { uint16_t sz; struct netmessage msg; uint16_t pos, len; }; EXPORT int netmessage_buffer_init(struct netmessage_buffer **); EXPORT int netmessage_buffer_flush(const int, struct netmessage_buffer *); EXPORT int netmessage_scrambler_init(void); EXPORT int netmessage_send(const int, const enum netmessage_type, const void *, const uint16_t, struct netmessage_buffer *); EXPORT int netmessage_recv(const int, struct netmessage *, const uint16_t, struct netmessage_buffer *); EXPORT int netmessage_get_hdr_stamp(const struct netmessage *, struct timeval *); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/helpers.c0000644000175000017500000000753311131700201014330 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include "helpers.h" // This linearised bounce back model expects small timesteps to work fine. /* EXPORT int bounce_ball_linear(igame, pos_old, dir_old, mu, mp, mw) struct gameplay* igame; float *pos_old, *dir_old; float mu, mp, mw; // drag on paddle, multipliers for paddle and wall velocity { int l_min= (-1); float alpha_min; // To detect first hit with wall float alpha,beta,det; // Add paddles to wall struct int nl= 0; //igame->geo.num_lines; // paddles for (int idx= 0; idx<= 1; idx++) { igame->geo.start[nl +idx][0]= (float)idx; igame->geo.start[nl +idx][1]= igame->pad[idx].mean + 0.5*igame->pad[idx].size; igame->geo.end[nl +idx][0]= (float)idx; igame->geo.end[nl +idx][1]= igame->pad[idx].mean - 0.5*igame->pad[idx].size; igame->geo.normal[nl +idx][0]= idx? (-1.f):1.f; igame->geo.normal[nl +idx][1]= 0.0; } float m11= igame->ball.pos[0] - pos_old[0]; float m21= igame->ball.pos[1] - pos_old[1]; for (int l= 0; l< nl+2; ++l) { // check for each line if there is an intersection with // the linearised trajectory of the ball // this results in a 2x2 linear system of equations. float m12= igame->geo.end[l][0] - igame->geo.start[l][0]; float m22= igame->geo.end[l][1] - igame->geo.start[l][1]; det= m11*m22 - m12*m21; if ( abs(det) > GAME_NUM_EPS ) { // if not, trajectory is parallel to wall float r1= igame->geo.start[l][0] - pos_old[0]; float r2= igame->geo.start[l][1] - pos_old[1]; // solve | m11 m12 | | alpha | = | r1 | // | m21 m22 | | beta | = | r2 | alpha= ( m22*r1 - m12*r2 )/det; beta = ( m11*r2 - m21*r1 )/det; // alpha_min detects the first hit for multiline enviroments if (GAME_NUM_EPSball.pos[0] - pos_old[0]); pos_old[1]+= alpha_min*(igame->ball.pos[1] - pos_old[1]); // calculate intermediate velocity: float dir[2],tmean; dir[0]= 0.5*(igame->ball.dir[0] + dir_old[0]); dir[1]= 0.5*(igame->ball.dir[1] + dir_old[1]); tmean= distance(igame->ball.pos,pos_old)/sqrt(dir[0]*dir[0]+dir[1]*dir[1]); // reflect mean velocity float vdn= dir[0]*igame->geo.normal[l_min][0] + dir[1]*igame->geo.normal[l_min][1]; if( l_min == nl ) { dir[0]= mp*(dir[0]-2.*vdn*igame->geo.normal[l_min][0]); dir[1]= mp*(dir[1]-2.*vdn*igame->geo.normal[l_min][1]) + mu*igame->pad[0].dir; } if( l_min == nl+1 ) { dir[0]= mp*(dir[0]-2.*vdn*igame->geo.normal[l_min][0]); dir[1]= mp*(dir[1]-2.*vdn*igame->geo.normal[l_min][1]) + mu*igame->pad[1].dir; } if( l_min < nl ) { dir[0]= mw*(dir[0]-2.*vdn*igame->geo.normal[l_min][0]); dir[1]= mw*(dir[1]-2.*vdn*igame->geo.normal[l_min][1]); } dir_old[0]= dir[0]; dir_old[1]= dir[1]; igame->ball.dir[0]= dir[0]; igame->ball.dir[1]= dir[1]; igame->ball.pos[0]= dir[0]*tmean + pos_old[0]; igame->ball.pos[0]= dir[0]*tmean + pos_old[0]; } else { pos_old[0]= igame->ball.pos[0]; pos_old[1]= igame->ball.pos[1]; } return PONG_SUCCESS; } */ mmpong-0.9.1/lib/config.h.in0000644000175000017500000000147611131661577014573 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __VERSION_H__ #define __VERSION_H__ #define VER_MAJ 0 #define VER_MIN 9 #define VER_SVN "${SVN_REV}" #define RESPATH "${LIB_RES_PATH}" #endif mmpong-0.9.1/lib/padround.c0000644000175000017500000000250611130115772014511 0ustar andreandre/* Copyright (C) 2008 Jan Friederich, Steffen Basting This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include "padround.h" #define BOW 0.2f // circle: 1.0, flat: 0.0f EXPORT int round_padprofile(where, ball) const float where[2]; struct gameball *ball; { // the circle is centered behind the middle of the paddle float n[2], scp; n[0] = (ball->pos[0] > 0.5f ? -1.f : 1.f) * cosf(M_PI * BOW * (where[1]-0.5f)); n[1] = sinf(M_PI * BOW * (where[1]-0.5f)); // Compute n * dir int idx; for (idx= 0, scp= 0; idx <= 1; idx++) scp+= n[idx] * ball->dir[idx]; for (idx= 0; idx <= 1; idx++) ball->dir[idx]-= 2.f * scp * n[idx]; return PONG_SUCCESS; } mmpong-0.9.1/lib/badminton.c0000644000175000017500000000465711131154266014663 0ustar andreandre#include #include "badminton.h" #include "helpers.h" // model constants #define BADMINTON_DRAG 1.23 // 0.5*c_w*A*rho/m #define BADMINTON_GRAV -9.81 // self-explanatory EXPORT int badminton_model(igame, timediff, paddle_action) struct gameplay *igame; float timediff; int (*paddle_action)(const float[2], struct gameball *); { if (igame->status != gamestatus_running) return PONG_ARGINVALID; // save old values float pos_old[2]; float dir_old[2]; for (int idx= 0; idx <= 1; idx++) { pos_old[idx]= igame->ball.pos[idx]; dir_old[idx]= igame->ball.dir[idx]; } // motion // one step of Runge Kutta 4 // I write the algorithm explicitly (for efficiency) float k[4][4],dir[2],dirnorm; //k[0] dir[0]= dir_old[0]; dir[1]= dir_old[1]; dirnorm= sqrtf(dir[0]*dir[0] + dir[1]*dir[1]); k[0][0]= - BADMINTON_DRAG*dirnorm*dir[0]; k[0][1]= BADMINTON_GRAV - BADMINTON_DRAG*dirnorm*dir[1]; k[0][2]= dir[0]; k[0][3]= dir[1]; //k[1] & k[2] for (int idx= 1; idx <= 2; idx++) { dir[0]= dir_old[0] + 0.5 *timediff *k[idx-1][0]; dir[1]= dir_old[1] + 0.5 *timediff *k[idx-1][1]; dirnorm= sqrtf(dir[0]*dir[0] + dir[1]*dir[1]); k[idx][0]= - BADMINTON_DRAG*dirnorm*dir[0]; k[idx][1]= BADMINTON_GRAV - BADMINTON_DRAG*dirnorm*dir[1]; k[idx][2]= dir[0]; k[idx][3]= dir[1]; } //k[3] dir[0]= dir_old[0] + timediff*k[2][0]; dir[1]= dir_old[1] + timediff*k[2][1]; dirnorm= sqrtf(dir[0]*dir[0] + dir[1]*dir[1]); k[3][0]= - BADMINTON_DRAG*dirnorm*dir[0]; k[3][1]= BADMINTON_GRAV - BADMINTON_DRAG*dirnorm*dir[1]; k[3][2]= dir[0]; k[3][3]= dir[1]; // now i can compute the new values. for (int idx= 0; idx< 2; idx++) { igame->ball.dir[idx]+= (k[0][idx] +k[1][idx] +k[2][idx] +k[3][idx]) *timediff/6.f; igame->ball.pos[idx]+= (k[0][idx+2] +k[1][idx+2] +k[2][idx+2] +k[3][idx+2]) *timediff/6.f; } // Check for bouncy things /* while( distance(pos_old,igame->ball.pos) > GAME_NUM_EPS ) bounce_ball_linear(igame,pos_old,dir_old,0.5,2.0,1.0); */ // Check if ball is outside and handle the situation int out=outside(&igame->ball); if( out ){ if( out == -1 ) igame->pad_attr[0].score++; if( out == 1 ) igame->pad_attr[1].score++; return PONG_SCORE; } return PONG_SUCCESS; } mmpong-0.9.1/lib/padflat.h0000644000175000017500000000164111130164267014317 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __FLAT_PADPROFILE_HEADER__ #define __FLAT_PADPROFILE_HEADER__ #include "game.h" #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif EXPORT int flat_padprofile(const float[2], struct gameball *); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/padround.h0000644000175000017500000000164411130164267014523 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __ROUND_PADPROFILE_HEADER__ #define __ROUND_PADPROFILE_HEADER__ #include "game.h" #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif EXPORT int round_padprofile(const float[2], struct gameball *); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/message.c0000644000175000017500000003130311132455474014326 0ustar andreandre/* Copyright (C) 2008 Kai Hertel, André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include #include "message.h" #ifdef WIN32 # include //typedef int (*iofunc)(SOCKET, char *, int, int); #else # include # include //typedef int (*iofunc)(int, void *, int, int); #endif typedef typeof(recv) *iofunc; #define OFFSET(base, member) ((int)( ((void *)&(member)) - ((void *)&(base)) )) #define INSTANCE(var, offset) ( ((void *)&(var)) + (offset) ) #define REGISTER(mode, member) { mode, OFFSET(nmsgbuf, nmsgbuf.member), sizeof(nmsgbuf.member) } #define ENDMARKER(mode) { mode, (-1), 0 } // the following one is to address a nasty compiler bug in 4.2 series GCCs #define REGISTER_ARR(mode, member, idx) { mode, OFFSET(nmsgbuf, nmsgbuf.member[0]) + idx*sizeof(nmsgbuf.member[idx]), sizeof(nmsgbuf.member[idx]) } // this one mainly exists for making the constant table below work elegantly // (in order to allow for the access macros above to have a reference to base calculations on) // the sendmessage() function could be easily changed to use a local instance instead of this TLS one, ... and it has static struct netmessage nmsgbuf; static struct timeval scrambler_salt= { .tv_sec= 0 }; static const struct { const enum netmessage_type id; /* 0 == generic, (-1) == terminal */ const int offset; const int size; } byte_order[]= { // seems to be getting a little out of hand... REGISTER (0, hdr.id), REGISTER (0, hdr.len), REGISTER (0, hdr.stamp.tv_sec), REGISTER (0, hdr.stamp.tv_usec), REGISTER (NETMSG_POS, payload.position), ENDMARKER (NETMSG_POS), REGISTER (NETMSG_UPDT, payload.part.stamp.tv_sec), REGISTER (NETMSG_UPDT, payload.part.stamp.tv_usec), REGISTER_ARR (NETMSG_UPDT, payload.part.ball.pos, 0), REGISTER_ARR (NETMSG_UPDT, payload.part.ball.pos, 1), REGISTER_ARR (NETMSG_UPDT, payload.part.ball.dir, 0), REGISTER_ARR (NETMSG_UPDT, payload.part.ball.dir, 1), REGISTER (NETMSG_UPDT, payload.part.pad[0].mean), REGISTER (NETMSG_UPDT, payload.part.pad[0].var), REGISTER (NETMSG_UPDT, payload.part.pad[0].size), // REGISTER (NETMSG_UPDT, payload.part.pad[0].dir), REGISTER (NETMSG_UPDT, payload.part.pad[1].mean), REGISTER (NETMSG_UPDT, payload.part.pad[1].var), REGISTER (NETMSG_UPDT, payload.part.pad[1].size), // REGISTER (NETMSG_UPDT, payload.part.pad[1].dir), ENDMARKER (NETMSG_UPDT), REGISTER (NETMSG_STAT, payload.full.team), REGISTER (NETMSG_STAT, payload.full.game.version), REGISTER (NETMSG_STAT, payload.full.game.stamp.tv_sec), REGISTER (NETMSG_STAT, payload.full.game.stamp.tv_usec), REGISTER (NETMSG_STAT, payload.full.game.mode), REGISTER (NETMSG_STAT, payload.full.game.status), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[0].score), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[0].peers), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[0].profile), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[1].score), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[1].peers), REGISTER (NETMSG_STAT, payload.full.game.pad_attr[1].profile), REGISTER_ARR (NETMSG_STAT, payload.full.game.ball.pos, 0), REGISTER_ARR (NETMSG_STAT, payload.full.game.ball.pos, 1), REGISTER_ARR (NETMSG_STAT, payload.full.game.ball.dir, 0), REGISTER_ARR (NETMSG_STAT, payload.full.game.ball.dir, 1), REGISTER (NETMSG_STAT, payload.full.game.pad[0].mean), REGISTER (NETMSG_STAT, payload.full.game.pad[0].var), REGISTER (NETMSG_STAT, payload.full.game.pad[0].size), // REGISTER (NETMSG_STAT, payload.full.game.pad[0].dir), REGISTER (NETMSG_STAT, payload.full.game.pad[1].mean), REGISTER (NETMSG_STAT, payload.full.game.pad[1].var), REGISTER (NETMSG_STAT, payload.full.game.pad[1].size), // REGISTER (NETMSG_STAT, payload.full.game.pad[1].dir), ENDMARKER(-1) }, scramble_fields[]= { REGISTER (0, hdr.stamp), REGISTER (NETMSG_UPDT, payload.part.stamp), REGISTER (NETMSG_STAT, payload.full.game.stamp), ENDMARKER(-1) }; static inline int io_raw(const int, void *, uint16_t *, const uint16_t, iofunc); static inline int apply_byte_order(uint16_t, struct netmessage *, int (*)(void *, const int)); static inline int convert_to_network(void *, const int); static inline int convert_to_host(void *, const int); static inline int scramble_stamps(const struct timeval *, struct netmessage *); EXPORT int netmessage_send(sock, msgid, data, datalen, peerbuf) const int sock; const enum netmessage_type msgid; const void *data; const uint16_t datalen; struct netmessage_buffer *peerbuf; { struct netmessage stage; int retcode= netmessage_buffer_flush(sock, peerbuf); if ((retcode == NETMSG_FAIL_SOCKET) || (retcode == NETMSG_END_SOCKET)) return retcode; // these conditions are considered critical and should take precedence over other issues if ((datalen > sizeof(stage.payload)) || (datalen && !data)) return NETMSG_ARGINVALID; if ((retcode != NETMSG_SUCCESS) && (retcode != NETMSG_ARGINVALID)) { if (retcode == NETMSG_PARTIAL) return NETMSG_FAIL_DELIVER; return retcode; } // else: flushed or nothing to flush // assemble header and fill in payload stage.hdr.id= msgid; uint16_t reqlen= stage.hdr.len= sizeof(stage.hdr) + datalen; struct timeval stamp; if (gettimeofday(&stamp, NULL)) { perror("gettimeofday()"); return NETMSG_FAIL_SYSCRITICAL; } stage.hdr.stamp.tv_sec = (uint32_t)stamp.tv_sec; stage.hdr.stamp.tv_usec = (uint32_t)stamp.tv_usec; if (datalen) memcpy(&stage.payload, data, reqlen - sizeof(stage.hdr)); // prepare and try sending out data if (scrambler_salt.tv_sec >0) if (scramble_stamps(&scrambler_salt, &stage) != NETMSG_SUCCESS) { fprintf(stderr, "Error: Cannot scramble time stamps.\n"); return NETMSG_FAIL_CONVERT; } if (apply_byte_order(msgid, &stage, convert_to_network) != NETMSG_SUCCESS) { fprintf(stderr, "Error: Cannot convert data to network byte order.\n"); return NETMSG_FAIL_CONVERT; } uint16_t len= 0; retcode= io_raw(sock, &stage, &len, reqlen, (iofunc)send); if ((retcode == NETMSG_FAIL_SOCKET) || (retcode == NETMSG_END_SOCKET)) return retcode; // make sure messages won't get cut off when the send buffer fills up if ((len != reqlen) && (peerbuf) && (peerbuf->sz >= reqlen)) { memcpy( ((char *)(&peerbuf->msg)) + len, ((char *)(&stage)) + len, reqlen - len ); peerbuf->pos= len; peerbuf->len= reqlen; return NETMSG_PARTIAL; } return retcode; } EXPORT int netmessage_recv(sock, rmsg, rmsglen, peerbuf) const int sock; struct netmessage *rmsg; const uint16_t rmsglen; struct netmessage_buffer *peerbuf; { if (sizeof(struct netmessage) > SSIZE_MAX) return NETMSG_FAIL_SYSCRITICAL; if (!rmsg || (sizeof(rmsg->hdr) > rmsglen)) return NETMSG_ARGINVALID; uint16_t pos= 0, *ppos= &pos; const uint16_t *plen= &rmsglen; struct netmessage *pmsg= rmsg; if (peerbuf) { if (sizeof(rmsg->hdr) > peerbuf->sz) return NETMSG_ARGINVALID; ppos= &peerbuf->pos; plen= &peerbuf->sz; pmsg= &peerbuf->msg; } // process the fixed-size header first int retcode; if ( (*ppos < sizeof(pmsg->hdr)) && ((retcode= io_raw(sock, pmsg, ppos, sizeof(pmsg->hdr), (iofunc)recv)) != NETMSG_SUCCESS) ) return retcode; // validate and process the variable-size payload uint16_t reqlen= ntohs(pmsg->hdr.len); // special case if ( (reqlen < sizeof(pmsg->hdr)) || (reqlen > sizeof(struct netmessage)) ) return NETMSG_FAIL_CHECKSUM; // packet bounds checking if (reqlen > *plen) return NETMSG_ARGINVALID; retcode= io_raw(sock, pmsg, ppos, reqlen, (iofunc)recv); if (retcode == NETMSG_FAIL_DELIVER) return NETMSG_PARTIAL; if (retcode != NETMSG_SUCCESS) return retcode; if (*ppos != reqlen) return NETMSG_FAIL_CHECKSUM; if (peerbuf) { memcpy(rmsg, pmsg, *ppos); *ppos= 0; pmsg= rmsg; } if (apply_byte_order(ntohs(pmsg->hdr.id), pmsg, convert_to_host) != NETMSG_SUCCESS) { fprintf(stderr, "Error: Cannot convert data to host byte order.\n"); return NETMSG_FAIL_CONVERT; } return NETMSG_SUCCESS; } // take care of bunched-up messages EXPORT int netmessage_buffer_flush(sock, peerbuf) const int sock; struct netmessage_buffer *peerbuf; { if (!peerbuf) return NETMSG_ARGINVALID; if (peerbuf->pos >= peerbuf->len) return NETMSG_SUCCESS; int retcode= io_raw(sock, &peerbuf->msg, &peerbuf->pos, peerbuf->len, (iofunc)send); if (peerbuf->pos == peerbuf->len) peerbuf->pos= peerbuf->len= 0; // clean up, optional at this point return retcode; } static inline int io_raw(sock, buf, pos, len, iofct) const int sock; void *buf; uint16_t *pos; const uint16_t len; iofunc iofct; { if (!buf || !pos || *pos > len || !iofct) return NETMSG_ARGINVALID; int loc, org= *pos; while (*pos < len) { errno= 0; loc= iofct(sock, ((char *)buf) + *pos, len - *pos, 0); if (errno && (errno != EINTR) && (errno != EAGAIN)) { // serious error perror("write()"); return NETMSG_FAIL_SOCKET; } if (!loc) // usually EOF return NETMSG_END_SOCKET; if (loc> 0) *pos += loc; #ifdef WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) return (org != *pos)? NETMSG_PARTIAL : NETMSG_FAIL_DELIVER; #else if (errno == EAGAIN) // message has not transferred completely return (org != *pos)? NETMSG_PARTIAL : NETMSG_FAIL_DELIVER; #endif } return NETMSG_SUCCESS; } static inline int apply_byte_order(msgid, pmsg, convert) uint16_t msgid; struct netmessage *pmsg; int (*convert)(void *, const int); { // take care of network byte order for specific message types for (int idx= 0; byte_order[idx].id != ((enum netmessage_type)(-1)); idx++) if ((msgid == byte_order[idx].id) || (0 == byte_order[idx].id)) { if (byte_order[idx].offset == (-1)) break; // shortcut to reduce average loop times if (convert( INSTANCE(*pmsg, byte_order[idx].offset), byte_order[idx].size ) != NETMSG_SUCCESS) { fprintf(stderr, "Error: Cannot convert data to host byte order.\n"); return NETMSG_FAIL_CONVERT; } } return NETMSG_SUCCESS; } static inline int convert_to_network(data, size) void *data; int size; { switch(size) { case 1: // just in case break; case sizeof(uint16_t): *((uint16_t *)data)= htons( *((uint16_t *)data) ); break; case sizeof(uint32_t): *((uint32_t *)data)= htonl( *((uint32_t *)data) ); break; default: return NETMSG_FAIL_CONVERT; } return NETMSG_SUCCESS; } static inline int convert_to_host(data, size) void *data; int size; { switch(size) { case 1: // just in case break; case sizeof(uint16_t): *((uint16_t *)data)= ntohs( *((uint16_t *)data) ); break; case sizeof(uint32_t): *((uint32_t *)data)= ntohl( *((uint32_t *)data) ); break; default: return NETMSG_FAIL_CONVERT; } return NETMSG_SUCCESS; } EXPORT int netmessage_buffer_init(bufptr) struct netmessage_buffer **bufptr; { if ((!bufptr) || (*bufptr)) return NETMSG_ARGINVALID; *bufptr= calloc(1, sizeof(struct netmessage_buffer)); if (! *bufptr) return NETMSG_FAIL_SYSCRITICAL; (*bufptr)->sz= sizeof(struct netmessage); return NETMSG_SUCCESS; } EXPORT int netmessage_scrambler_init(void) { if (gettimeofday(&scrambler_salt, NULL)) { perror("gettimeofday()"); return NETMSG_FAIL_SYSCRITICAL; } if (!scrambler_salt.tv_sec) return NETMSG_FAIL_CHECKSUM; scrambler_salt.tv_sec= rand() % scrambler_salt.tv_sec; scrambler_salt.tv_usec= rand() % (1000L * 1000L); return NETMSG_SUCCESS; } static inline int scramble_stamps(salt, msg) const struct timeval *salt; struct netmessage *msg; { enum netmessage_type msgid= msg->hdr.id; for (int idx= 0; scramble_fields[idx].id != ((enum netmessage_type)(-1)); idx++) if ((msgid == scramble_fields[idx].id) || (0 == scramble_fields[idx].id)) { // if (scramble_fields[idx].offset == (-1)) break; struct gametime_public *val= INSTANCE(*msg, scramble_fields[idx].offset); if (val->tv_sec < salt->tv_sec) return NETMSG_FAIL_CONVERT; val->tv_sec-= salt->tv_sec; val->tv_usec-= salt->tv_usec; if (val->tv_usec < 0) { if (val->tv_sec < 1) { val->tv_usec= 0; return NETMSG_FAIL_CONVERT; } val->tv_sec--; val->tv_usec+= 1000L * 1000L; } } return NETMSG_SUCCESS; } EXPORT int netmessage_get_hdr_stamp(msg, stamp) const struct netmessage *msg; struct timeval *stamp; { if ((!msg) || (!stamp)) return NETMSG_ARGINVALID; stamp->tv_sec= msg->hdr.stamp.tv_sec; stamp->tv_usec= msg->hdr.stamp.tv_usec; return NETMSG_SUCCESS; } mmpong-0.9.1/lib/CMakeLists.txt0000644000175000017500000000432211132666275015302 0ustar andreandreSET( LIB_SOURCES badminton.c game.c helpers.c linear.c message.c padflat.c padround.c ) SET( LIB_HEADERS badminton.h dllhelper.h game.h helpers.h linear.h message.h padflat.h padround.h ) SET( LIB_LIBRARIES m ) SET( LIB_VER_MAJ 0 ) SET( LIB_VER_MIN 9 ) SET( SVN_REV "${MMPONG_WC_REVISION}" ) IF(CMAKE_SYSTEM_NAME STREQUAL "Windows") SET( LIB_LIBRARIES ${LIB_LIBRARIES} ws2_32) ADD_DEFINITIONS( -DMMPONG_BUILD_DLL) ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Windows") SET( CONFIGURE_DIR "${CMAKE_CURRENT_BINARY_DIR}") CONFIGURE_FILE ( config.h.in ${CONFIGURE_DIR}/config.h ) CONFIGURE_FILE ( mmpong.pc.in ${CONFIGURE_DIR}/mmpong.pc @ONLY ) #shared ADD_LIBRARY( libmmpong SHARED ${LIB_SOURCES} ) SET_TARGET_PROPERTIES( libmmpong PROPERTIES OUTPUT_NAME "mmpong" CLEAN_DIRECT_OUTPUT 1 SOVERSION "${LIB_VER_MAJ}.${LIB_VER_MIN}" VERSION "${LIB_VER_MAJ}.${LIB_VER_MIN}" PUBLIC_HEADER "${LIB_HEADERS}" ) TARGET_LINK_LIBRARIES( libmmpong ${LIB_LIBRARIES}) #static ADD_LIBRARY( libmmpong-static STATIC ${LIB_SOURCES} ) SET_TARGET_PROPERTIES( libmmpong-static PROPERTIES OUTPUT_NAME "mmpong" CLEAN_DIRECT_OUTPUT 1 SOVERSION "${LIB_VER_MAJ}.${LIB_VER_MIN}" VERSION "${LIB_VER_MAJ}.${LIB_VER_MIN}" ) TARGET_LINK_LIBRARIES( libmmpong-static ${LIB_LIBRARIES}) IF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(TARGETS libmmpong libmmpong-static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include/mmpong ) ELSEIF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(TARGETS libmmpong libmmpong-static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) ENDIF(NOT CMAKE_MAJOR_VERSION EQUAL 2 OR NOT CMAKE_MINOR_VERSION EQUAL 4) INSTALL(FILES ${CONFIGURE_DIR}/mmpong.pc DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig ) IF(APPLE) SET_TARGET_PROPERTIES( libmmpong PROPERTIES FRAMEWORK TRUE) ADD_CUSTOM_COMMAND(TARGET libmmpong POST_BUILD COMMAND install_name_tool -id @executable_path/../Frameworks/mmpong.framework/Versions/${LIB_VER_MAJ}.${LIB_VER_MIN}/mmpong ${CMAKE_BINARY_DIR}/mmpong.framework/mmpong ) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/mmpong.framework ) ENDIF(APPLE) mmpong-0.9.1/lib/badminton.h0000644000175000017500000000171111130164267014655 0ustar andreandre/* Copyright (C) 2008 Kai Hertel, rtr This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __BADMINTON_MODEL_HEADER__ #define __BADMINTON_MODEL_HEADER__ #include "dllhelper.h" #include "game.h" #ifdef __cplusplus extern "C" { #endif EXPORT int badminton_model(struct gameplay *, float, int (*)(const float[2], struct gameball *)); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/padflat.c0000644000175000017500000000176011130115772014311 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include "padflat.h" EXPORT int flat_padprofile(where, ball) const float where[2]; struct gameball *ball; { // for the flat profile, the paddle is pretty much irrelevant once we have determined that it actually hit the ball ball->dir[0]*= (-1.f); // ball->pos[0]= (2.f * (float)where[0]) - ball->pos[0]; return PONG_SUCCESS; } mmpong-0.9.1/lib/dllhelper.h0000644000175000017500000000161411131645643014662 0ustar andreandre/* Copyright (C) 2008 André Gaul This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifdef WIN32 # ifdef MMPONG_BUILD_DLL //DLL export # define EXPORT __declspec(dllexport) # else //EXE import # define EXPORT __declspec(dllimport) # endif #else //nothing special in non-WIN32-case # define EXPORT #endif mmpong-0.9.1/lib/linear.h0000644000175000017500000000167311130164267014163 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __LINEAR_MODEL_HEADER__ #define __LINEAR_MODEL_HEADER__ #include "game.h" #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif EXPORT int linear_model(struct gameplay *, float, int (*)(const float[2], struct gameball *)); #ifdef __cplusplus } #endif #endif mmpong-0.9.1/lib/mmpong.pc.in0000644000175000017500000000044111132372353014755 0ustar andreandreprefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include Name: mmpong Description: massively multiplayer pong game Version: @LIB_VER_MAJ@.@LIB_VER_MIN@ URL: http://www.mmpong.net Libs: -L${libdir} -lmmpong Cflags: -I${includedir}/mmpong mmpong-0.9.1/lib/linear.c0000644000175000017500000000467311130115772014156 0ustar andreandre#include "linear.h" #include "helpers.h" #include EXPORT int linear_model(igame, timediff, paddle_action) struct gameplay *igame; float timediff; int (*paddle_action)(const float[2], struct gameball *); { int lasttouch= (-1); if (igame->status != gamestatus_running) return PONG_ARGINVALID; // motion for (int idx= 0; idx <= 1; idx++) igame->ball.pos[idx]+= igame->ball.dir[idx] * timediff; // reflections (in the right order) int condition= PONG_SUCCESS; while (outside(&igame->ball)) { if (outby(igame->ball.pos[0]) * igame->ball.dir[0] > outby(igame->ball.pos[1]) * igame->ball.dir[1]) { // determine where the ball had passed the paddle int border= (igame->ball.pos[0] > .5f); if (fabs(igame->ball.dir[0]) < GAME_NUM_EPS) return PONG_INTERROR; // try avoiding endless loops caused by paddle implementation issues if (lasttouch == border) return PONG_INTERROR; lasttouch= border; float crosstime= ( ((float)border) - igame->ball.pos[0] ) /igame->ball.dir[0]; // solve linear equation if (crosstime >GAME_NUM_EPS) return PONG_INTERROR; // back through time if (hit(&igame->pad[border], igame->ball.pos[1] + igame->ball.dir[1] *crosstime)) { // bounce off the paddle float cpos_abs= igame->ball.pos[1] + igame->ball.dir[1] *crosstime; float where[2]= { border, 0.5 }; // x coordinate if (igame->pad[border].size >0) // y coordinate relative to its size where[1]= (cpos_abs - igame->pad[border].mean + igame->pad[border].size/2) / igame->pad[border].size; if ((condition= paddle_action(where, &igame->ball)) != PONG_SUCCESS) return condition; // compute the new ball position after the direction and velocity have been set by paddle_action() where[1]= cpos_abs; for (int idx= 0; idx<= 1; idx++) // forward from the time of collision based on the new motion vector igame->ball.pos[idx]= where[idx] + igame->ball.dir[idx] *(-crosstime); condition= PONG_PADHIT; continue; } // end the game igame->pad_attr[!border].score++; // <-- the model decides on the scoring system return PONG_SCORE; } else { // bounce off the wall int border= (igame->ball.dir[1] > 0); igame->ball.pos[1]= (2.f * (float)border) - igame->ball.pos[1]; igame->ball.dir[1]*= (-1.f); // try avoiding endless loops caused by paddle implementation issues if (lasttouch == border+2) return PONG_INTERROR; lasttouch= border+2; } } return condition; } mmpong-0.9.1/lib/game.c0000644000175000017500000003025011131730016013576 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #include #include #include #include #include "game.h" #include "message.h" #include "linear.h" #include "badminton.h" #include "padflat.h" #include "padround.h" #define INIT_RANGE_X 0.2 #define INIT_RANGE_Y 0.6 #define INIT_RANGE_ANGLE 0.5 #define INIT_PADDLE_SIZE 0.25 #define INIT_VELOCITY 0.6 #define STALL_TIMEOUT 5.0 static int gameplay_newgame(struct gameplay *); //static int set_geometry(const enum gamemode, struct gamegeometry *); static const struct { enum gamemode mode; const char *spell; int (*fct)(struct gameplay *, float, int (*)(const float[2], struct gameball *)); } gamemode_handler[]= { { gamemode_linear, "linear", linear_model }, { gamemode_badminton, "badminton", badminton_model }, { (-1), NULL, NULL } }; static const struct { enum gamepadprofile mode; const char *spell; int (*fct)(const float[2], struct gameball *); } gamepadprofile_handler[]= { { gamepadprofile_flat, "flat", flat_padprofile }, { gamepadprofile_round, "round", round_padprofile }, { (-1), NULL, NULL } }; EXPORT short gameplay_getversion(void) { return sizeof(struct gameplay); } EXPORT struct gameplay *gameplay_create(void) { struct gameplay *instance= calloc(1, sizeof(struct gameplay)); if (instance) instance->version= gameplay_getversion(); return instance; } EXPORT int gameplay_init(igame, size, model, profile) struct gameplay *igame; int size; const enum gamemode model; const enum gamepadprofile profile; { if (!igame) return PONG_ARGINVALID; if (size < sizeof(struct gameplay)) return PONG_UNSUPPORTED; // check for valid model implementations int mode; for (mode= 0; gamemode_handler[mode].fct; mode++) if (gamemode_handler[mode].mode == model) break; if (!gamemode_handler[mode].fct) return PONG_UNSUPPORTED; for (mode= 0; gamepadprofile_handler[mode].fct; mode++) if (gamepadprofile_handler[mode].mode == profile) break; if (!gamepadprofile_handler[mode].fct) return PONG_UNSUPPORTED; memset(igame, 0, sizeof(struct gameplay)); // set headers igame->version= sizeof(struct gameplay); igame->mode= model; // set paddles for (int idx= 0; idx <= 1; idx++) { igame->pad[idx].mean= 0.5f; igame->pad[idx].var= 0; igame->pad[idx].size= INIT_PADDLE_SIZE; igame->pad_attr[idx].profile= profile; // we have to set this value somewhere later in the game. but i have no idea where. // igame->pad[idx].dir= 0.f; // score has been nulled by memset } // if ((mode= set_geometry(model, &igame->geo)) != PONG_SUCCESS) // return mode; return gameplay_newgame(igame); } static int gameplay_newgame(igame) struct gameplay *igame; { if (gettimeofday(&igame->stamp, NULL) == (-1)) return PONG_INTERROR; memcpy(&igame->lasthit, &igame->stamp, sizeof(igame->lasthit)); igame->status= gamestatus_running; // set ball pos igame->ball.pos[0]= ((float)rand())/RAND_MAX * INIT_RANGE_X + 0.5 - INIT_RANGE_X*0.5; igame->ball.pos[1]= ((float)rand())/RAND_MAX * INIT_RANGE_Y + 0.5 - INIT_RANGE_Y*0.5; float angle = ( ((float)rand())/RAND_MAX * 0.5 - 0.25 + (rand()%2) ) * M_PI; igame->ball.dir[0]= cosf(angle) * INIT_VELOCITY; igame->ball.dir[1]= sinf(angle) * INIT_VELOCITY; return PONG_SUCCESS; } EXPORT int gameplay_update(igame, newtime) struct gameplay *igame; const struct timeval *newtime; { if ((!igame) || (!newtime)) return PONG_ARGINVALID; if (igame->status != gamestatus_running) return gameplay_newgame(igame); float timediff= ((float)( newtime->tv_sec - igame->stamp.tv_sec)) + ((float)( newtime->tv_usec - igame->stamp.tv_usec)) /1000.0f /1000.0f; if (timediff < 0) return PONG_ARGINVALID; // we only move forward in time (especially in non-linear models) float stalldiff= ((float)( newtime->tv_sec - igame->lasthit.tv_sec)) + ((float)( newtime->tv_usec - igame->lasthit.tv_usec)) /1000.0f /1000.0f; if (stalldiff >= STALL_TIMEOUT) { igame->status= gamestatus_stall; return PONG_SUCCESS; } // find model implementation int model; for (model= 0; gamemode_handler[model].fct; model++) if (igame->mode == gamemode_handler[model].mode) break; if (gamemode_handler[model].fct == NULL) return PONG_UNSUPPORTED; memcpy(&igame->stamp, newtime, sizeof(igame->stamp)); enum gamepadprofile reqprofile= igame->pad_attr[0].profile; int padprofile; for (padprofile= 0; padprofile<= 1; padprofile++) // require all paddles to use the same profile (for now) if (igame->pad_attr[padprofile].profile != reqprofile) return PONG_UNSUPPORTED; // ... so far for (padprofile= 0; gamepadprofile_handler[padprofile].fct; padprofile++) if (gamepadprofile_handler[padprofile].mode == reqprofile) break; if (gamepadprofile_handler[padprofile].fct == NULL) return PONG_UNSUPPORTED; // struct gameplay gameintern; int retcode; // if ((retcode= gameplay_public_to_internal(game, newtime, &gameintern)) != PONG_SUCCESS) return retcode; retcode= gamemode_handler[model].fct(igame, timediff, gamepadprofile_handler[padprofile].fct); switch (retcode) { case PONG_PADHIT: memcpy(&igame->lasthit, newtime, sizeof(igame->lasthit)); break; case PONG_SCORE: igame->status= gamestatus_onhold; // leave the score counting to the model (not necessarily linear) break; case PONG_SUCCESS: break; default: if (retcode == PONG_INTERROR) // reset the game (which will hopefully help the situation) igame->status= gamestatus_onhold; return retcode; } // if ((retcode= gameplay_internal_to_public(&gameintern, newtime, game)) != PONG_SUCCESS) return retcode; return PONG_SUCCESS; // mask internal state } EXPORT int gameplay_public_to_internal(pgame, igame) const struct gameplay_public *pgame; struct gameplay *igame; { if ((!pgame) || (!igame)) return PONG_ARGINVALID; igame->stamp.tv_sec = pgame->stamp.tv_sec; igame->stamp.tv_usec = pgame->stamp.tv_usec; igame->mode= pgame->mode; igame->status= pgame->status; // ball for (int idx= 0; idx <= 1; idx++) { igame->ball.pos[idx]= ((float)(pgame->ball.pos[idx] % PONG_RANGE_SPREAD)) /PONG_RANGE_SPREAD; igame->ball.dir[idx]= (((float)(pgame->ball.dir[idx] % PONG_RANGE_SPREAD)) /PONG_RANGE_SPREAD *2.f -1.f) * GAMEBALL_DIR_MAX; } // paddles for (int idx= 0; idx <= 1; idx++) { igame->pad[idx].mean= ((float)(pgame->pad[idx].mean % PONG_RANGE_SPREAD)) /PONG_RANGE_SPREAD; igame->pad[idx].var= ((float)(pgame->pad[idx].var % PONG_RANGE_SPREAD)) /PONG_RANGE_SPREAD; igame->pad[idx].size= ((float)(pgame->pad[idx].size % PONG_RANGE_SPREAD)) /PONG_RANGE_SPREAD; igame->pad_attr[idx].score= pgame->pad_attr[idx].score; igame->pad_attr[idx].peers= pgame->pad_attr[idx].peers; igame->pad_attr[idx].profile= pgame->pad_attr[idx].profile; } return PONG_SUCCESS; } EXPORT int gameplay_internal_to_public(igame, pgame) const struct gameplay *igame; struct gameplay_public *pgame; { if ((!igame) || (!pgame)) return PONG_ARGINVALID; pgame->stamp.tv_sec = igame->stamp.tv_sec; pgame->stamp.tv_usec = igame->stamp.tv_usec; pgame->mode= igame->mode; pgame->status= igame->status; // ball for (int idx= 0; idx <= 1; idx++) { // cut off values and transform float crop= igame->ball.pos[idx]; if (crop > 1.f) crop= 1.f; if (crop < 0.f) crop= 0.f; pgame->ball.pos[idx]= (uint16_t)(crop * PONG_RANGE_SPREAD); crop= igame->ball.dir[idx]; if (crop > GAMEBALL_DIR_MAX) crop= GAMEBALL_DIR_MAX; if (crop < -GAMEBALL_DIR_MAX) crop= -GAMEBALL_DIR_MAX; pgame->ball.dir[idx]= (uint16_t)((crop + GAMEBALL_DIR_MAX) /2.f /GAMEBALL_DIR_MAX * PONG_RANGE_SPREAD); } // paddles for (int idx= 0; idx <= 1; idx++) { pgame->pad[idx].mean= (uint16_t)(igame->pad[idx].mean * PONG_RANGE_SPREAD); pgame->pad[idx].var= (uint16_t)(igame->pad[idx].var * PONG_RANGE_SPREAD); pgame->pad[idx].size= (uint16_t)(igame->pad[idx].size * PONG_RANGE_SPREAD); // pgame->pad[idx].dir= (short)(igame->pad[idx].dir * PONG_RANGE_SPREAD); pgame->pad_attr[idx].score= igame->pad_attr[idx].score; pgame->pad_attr[idx].peers= igame->pad_attr[idx].peers; pgame->pad_attr[idx].profile= igame->pad_attr[idx].profile; } return PONG_SUCCESS; } EXPORT const char *gameplay_spell(model, padprofile) const enum gamemode model; const enum gamepadprofile padprofile; { int idx; if (model >= 0) { for (idx= 0; gamemode_handler[idx].fct; idx++) if (gamemode_handler[idx].mode == model) return gamemode_handler[idx].spell; return NULL; } if (padprofile >= 0) { for (idx= 0; gamepadprofile_handler[idx].fct; idx++) if (gamepadprofile_handler[idx].mode == padprofile) return gamepadprofile_handler[idx].spell; return NULL; } return NULL; } EXPORT int gameplay_parse(spell, model, padprofile) const char *spell; enum gamemode *model; enum gamepadprofile *padprofile; { int idx; if (model) { for (idx= 0; gamemode_handler[idx].fct; idx++) if (!strcmp(gamemode_handler[idx].spell, spell)) { *model= gamemode_handler[idx].mode; return PONG_SUCCESS; } return PONG_UNSUPPORTED; } if (padprofile) { for (idx= 0; gamepadprofile_handler[idx].fct; idx++) if (!strcmp(gamepadprofile_handler[idx].spell, spell)) { *padprofile= gamepadprofile_handler[idx].mode; return PONG_SUCCESS; } return PONG_UNSUPPORTED; } return PONG_UNSUPPORTED; } EXPORT int gameplay_apply_state(msg, igame, team) const struct netmessage *msg; struct gameplay *igame; uint16_t *team; { if (!msg || !igame || !team) return PONG_ARGINVALID; struct gameplay_public const *pub= NULL; switch (msg->hdr.id) { case NETMSG_STAT: if (msg->hdr.len < (sizeof(struct netmessage_header) + sizeof(struct game_state_full))) return PONG_UNSUPPORTED; *team = msg->payload.full.team; // if (*team <0) *team= 0; if (*team >GAME_MAX_TEAMS) *team= GAME_MAX_TEAMS -1; pub= &msg->payload.full.game; break; case NETMSG_UPDT: if (msg->hdr.len < (sizeof(struct netmessage_header) + sizeof(struct game_state_part))) return PONG_UNSUPPORTED; struct gameplay_public game_full; gameplay_internal_to_public(igame, &game_full); memcpy (&game_full.ball, &msg->payload.part.ball, sizeof(game_full.ball)); memcpy (&game_full.pad, msg->payload.part.pad, 2*sizeof(game_full.pad)); pub= &game_full; break; } if (!pub) return PONG_ARGINVALID; gameplay_public_to_internal(pub, igame); memcpy (&igame->lasthit, &igame->stamp, sizeof(igame->lasthit)); // take a guess return PONG_SUCCESS; } /* static int set_geometry(mode, igeo) const enum gamemode mode; struct gamegeometry *igeo; { switch(mode) { case gamemode_linear: igeo->num_lines= 2; if( igeo->num_lines >= GAME_MAX_OBJECTS-2) return PONG_INTERROR; // bottom wall; igeo->start[0][0]= 0.; igeo->start[0][1]= 0.; igeo->end[0][0]= 1.; igeo->end[0][1]= 0.; // top wall; igeo->start[1][0]= 1.; igeo->start[1][1]= 1.; igeo->end[1][0]= 0.; igeo->end[1][1]= 1.; break; case gamemode_badminton: igeo->num_lines= 3; if( igeo->num_lines >= GAME_MAX_OBJECTS-2) return PONG_INTERROR; // bottom wall; igeo->start[0][0]= 0.; igeo->start[0][1]= 0.; igeo->end[0][0]= 1.; igeo->end[0][1]= 0.; // top wall; igeo->start[1][0]= 1.; igeo->start[1][1]= 1.; igeo->end[1][0]= 0.; igeo->end[1][1]= 1.; // net igeo->start[2][0]= 0.5; igeo->start[2][1]= 0.0; // maybe, we need to switch coordinate systems for an accurate physics mapping igeo->end[2][0]= 0.5; igeo->end[2][1]= 0.3; break; default: return PONG_UNSUPPORTED; } // Compute normals by the right hand rule: // Daumen schaut aus dem Monitor raus // Zeigefinger zeigt von startpunkt zu endpunkt // Mittelfinger schaut in Normalenrichtung for(int l= 0;l< igeo->num_lines; ++l) { igeo->normal[l][0]= -igeo->end[l][1] + igeo->start[l][1]; igeo->normal[l][1]= igeo->end[l][0] - igeo->start[l][0]; } return PONG_SUCCESS; } */ mmpong-0.9.1/lib/helpers.h0000644000175000017500000000311011130237411014327 0ustar andreandre/* Copyright (C) 2008 Kai Hertel This file is part of mmpong. mmpong is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. mmpong is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with mmpong. If not, see . */ #ifndef __HELPERS_HEADER__ #define __HELPERS_HEADER__ #include #include "game.h" #include "dllhelper.h" #ifdef __cplusplus extern "C" { #endif EXPORT int bounce_ball_linear(struct gameplay *, float *, float *, float, float, float); static inline int outside(ball) const struct gameball *ball; { if ((ball->pos[0] < 0) || (ball->pos[1] < 0)) return (-1); if ((ball->pos[0] > 1) || (ball->pos[1] > 1)) return 1; return 0; } static inline float outby(val) float val; { if (val < 0) return val; if (val > 1) return val -1; return 0; } static inline int hit(pad, pos) const struct gamepaddle *pad; float pos; { return (pad->mean - pad->size/2 <= pos) && (pad->mean + pad->size/2 >= pos); } static inline float distance(p,q) const float p[2], q[2]; { float scp= 0; for (int idx= 0; idx< 2; idx++) { float h= p[idx] - q[idx]; scp+= h * h; } return sqrtf(scp); } #ifdef __cplusplus } #endif #endif