diff --git a/src/base/detect.h~ b/src/base/detect.h~ new file mode 100644 index 000000000..04cca2d9c --- /dev/null +++ b/src/base/detect.h~ @@ -0,0 +1,115 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASELIB_FILE_CONFIG_H +#define BASELIB_FILE_CONFIG_H + +/* + this file detected the family, platform and architecture + to compile for. +*/ + +/* platforms */ + +/* windows Family */ +#if defined(WIN64) || defined(_WIN64) + /* Hmm, is this IA64 or x86-64? */ + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN64 1 + #define CONF_PLATFORM_STRING "win64" +#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN32 1 + #define CONF_PLATFORM_STRING "win32" +#endif + +/* unix family */ +#if defined(__FreeBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_FREEBSD 1 + #define CONF_PLATFORM_STRING "freebsd" +#endif + +#if defined(__OpenBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_OPENBSD 1 + #define CONF_PLATFORM_STRING "openbsd" +#endif + +#if defined(__LINUX__) || defined(__linux__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_LINUX 1 + #define CONF_PLATFORM_STRING "linux" +#endif + +#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_MACOSX 1 + #define CONF_PLATFORM_STRING "macosx" +#endif + +#if defined(__sun) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFROM_SOLARIS 1 + #define CONF_PLATFORM_STRING "solaris" +#endif + +/* beos family */ +#if defined(__BeOS) || defined(__BEOS__) + #define CONF_FAMILY_BEOS 1 + #define CONF_FAMILY_STRING "beos" + #define CONF_PLATFORM_BEOS 1 + #define CONF_PLATFORM_STRING "beos" +#endif + + +/* architectures */ +#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) + #define CONF_ARCH_IA32 1 + #define CONF_ARCH_STRING "ia32" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__ia64__) + #define CONF_ARCH_IA64 1 + #define CONF_ARCH_STRING "ia64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__amd64__) || defined(__x86_64__) + #define CONF_ARCH_AMD64 1 + #define CONF_ARCH_STRING "amd64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__powerpc__) || defined(__ppc__) + #define CONF_ARCH_PPC 1 + #define CONF_ARCH_STRING "ppc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + +#if defined(__sparc__) + #define CONF_ARCH_SPARC 1 + #define CONF_ARCH_STRING "sparc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + + +#ifndef CONF_FAMILY_STRING +#define CONF_FAMILY_STRING "unknown" +#endif + +#ifndef CONF_PLATFORM_STRING +#define CONF_PLATFORM_STRING "unknown" +#endif + +#ifndef CONF_ARCH_STRING +#define CONF_ARCH_STRING "unknown" +#endif + +#endif diff --git a/src/base/math.h~ b/src/base/math.h~ new file mode 100644 index 000000000..f3ba2ea87 --- /dev/null +++ b/src/base/math.h~ @@ -0,0 +1,58 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASE_MATH_H +#define BASE_MATH_H + +#include + +template +inline T clamp(T val, T min, T max) +{ + if(val < min) + return min; + if(val > max) + return max; + return val; +} + +inline float sign(float f) +{ + return f<0.0f?-1.0f:1.0f; +} + +inline int round(float f) +{ + if(f > 0) + return (int)(f+0.5f); + return (int)(f-0.5f); +} + +template +inline T mix(const T a, const T b, TB amount) +{ + return a + (b-a)*amount; +} + +inline float frandom() { return rand()/(float)(RAND_MAX); } + +// float to fixed +inline int f2fx(float v) { return (int)(v*(float)(1<<10)); } +inline float fx2f(int v) { return v*(1.0f/(1<<10)); } + +class fxp +{ + int value; +public: + void set(int v) { value = v; } + int get() const { return value; } + fxp &operator = (int v) { value = v<<10; return *this; } + fxp &operator = (float v) { value = (int)(v*(float)(1<<10)); return *this; } + operator float() const { return value/(float)(1<<10); } +}; + +const float pi = 3.1415926535897932384626433f; + +template inline T min(T a, T b) { return a inline T max(T a, T b) { return a>b?a:b; } +template inline T absolute(T a) { return a 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/src/base/system.c~ b/src/base/system.c~ new file mode 100644 index 000000000..fbf865e02 --- /dev/null +++ b/src/base/system.c~ @@ -0,0 +1,1595 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include +#include + +/* #include "detect.h" */ +#include "system.h" +/* #include "e_console.h" */ + +#include "errno.h" + +#if defined(CONF_FAMILY_UNIX) +#include +#include + + /* unix net includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONF_PLATFORM_MACOSX) +#include +#endif + +#elif defined(CONF_FAMILY_WINDOWS) +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to + work */ +#include +#include +#include +#include +#include +#include +#include + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#else +#error NOT IMPLEMENTED +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + + IOHANDLE io_stdin() + { + return (IOHANDLE) stdin; + } + IOHANDLE io_stdout() + { + return (IOHANDLE) stdout; + } + IOHANDLE io_stderr() + { + return (IOHANDLE) stderr; + } + + static DBG_LOGGER loggers[16]; + + static int num_loggers = 0; + + static NETSTATS network_stats = { 0 }; + static MEMSTATS memory_stats = { 0 }; + + void dbg_logger(DBG_LOGGER logger) + { + loggers[num_loggers++] = logger; + } + + void dbg_assert_imp(const char *filename, int line, int test, + const char *msg) + { + if (!test) + { + dbg_msg("assert", "%s(%d): %s", filename, line, msg); + dbg_break(); + } + } + + void dbg_break() + { + *((unsigned *)0) = 0x0; + } + + void dbg_msg(const char *sys, const char *fmt, ...) + { + va_list args; + + char str[1024 * 4]; + + char *msg; + + int i, len; + + str_format(str, sizeof(str), "[%08x][%s]: ", (int)time(0), sys); + len = strlen(str); + msg = (char *)str + len; + + va_start(args, fmt); +#if defined(CONF_FAMILY_WINDOWS) + _vsnprintf(msg, sizeof(str) - len, fmt, args); +#else + vsnprintf(msg, sizeof(str) - len, fmt, args); +#endif + va_end(args); + + for (i = 0; i < num_loggers; i++) + loggers[i] (str); + } + + static void logger_stdout(const char *line) + { + printf("%s\n", line); + fflush(stdout); + } + + static void logger_debugger(const char *line) + { +#if defined(CONF_FAMILY_WINDOWS) + OutputDebugString(line); + OutputDebugString("\n"); +#endif + } + + + static IOHANDLE logfile = 0; + + static void logger_file(const char *line) + { + io_write(logfile, line, strlen(line)); + io_write(logfile, "\n", 1); + io_flush(logfile); + } + + void dbg_logger_stdout() + { + dbg_logger(logger_stdout); + } + void dbg_logger_debugger() + { + dbg_logger(logger_debugger); + } + void dbg_logger_file(const char *filename) + { + logfile = io_open(filename, IOFLAG_WRITE); + if (logfile) + dbg_logger(logger_file); + else + dbg_msg("dbg/logger", "failed to open '%s' for logging", filename); + + } + /* */ + + int memory_alloced = 0; + + typedef struct MEMHEADER + { + const char *filename; + int line; + int size; + struct MEMHEADER *prev; + struct MEMHEADER *next; + } MEMHEADER; + + typedef struct MEMTAIL + { + int guard; + } MEMTAIL; + + static struct MEMHEADER *first = 0; + + static const int MEM_GUARD_VAL = 0xbaadc0de; + + void *mem_alloc_debug(const char *filename, int line, unsigned size, + unsigned alignment) + { + /* TODO: fix alignment */ + /* TODO: add debugging */ + MEMHEADER *header = + (struct MEMHEADER *)malloc(size + sizeof(MEMHEADER) + + sizeof(MEMTAIL)); + MEMTAIL *tail = (struct MEMTAIL *)(((char *)(header + 1)) + size); + + header->size = size; + header->filename = filename; + header->line = line; + + memory_stats.allocated += header->size; + memory_stats.total_allocations++; + memory_stats.active_allocations++; + + tail->guard = MEM_GUARD_VAL; + + header->prev = (MEMHEADER *) 0; + header->next = first; + if (first) + first->prev = header; + first = header; + + /* dbg_msg("mem", "++ %p", header+1); */ + return header + 1; + } + + void mem_free(void *p) + { + if (p) + { + MEMHEADER *header = (MEMHEADER *) p - 1; + + MEMTAIL *tail = + (MEMTAIL *) (((char *)(header + 1)) + header->size); + + if (tail->guard != MEM_GUARD_VAL) + dbg_msg("mem", "!! %p", p); + /* dbg_msg("mem", "-- %p", p); */ + memory_stats.allocated -= header->size; + memory_stats.active_allocations--; + + if (header->prev) + header->prev->next = header->next; + else + first = header->next; + if (header->next) + header->next->prev = header->prev; + + free(header); + } + } + + void mem_debug_dump() + { + char buf[1024]; + + MEMHEADER *header = first; + + IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); + + while (header) + { + str_format(buf, sizeof(buf), "%s(%d): %d\n", header->filename, + header->line, header->size); + io_write(f, buf, strlen(buf)); + header = header->next; + } + + io_close(f); + } + + + void mem_copy(void *dest, const void *source, unsigned size) + { + memcpy(dest, source, size); + } + + void mem_move(void *dest, const void *source, unsigned size) + { + memmove(dest, source, size); + } + + void mem_zero(void *block, unsigned size) + { + memset(block, 0, size); + } + + int mem_check_imp() + { + MEMHEADER *header = first; + + while (header) + { + MEMTAIL *tail = + (MEMTAIL *) (((char *)(header + 1)) + header->size); + if (tail->guard != MEM_GUARD_VAL) + { + dbg_msg("mem", "Memory check failed at %s(%d): %d", + header->filename, header->line, header->size); + return 0; + } + header = header->next; + } + + return 1; + } + + IOHANDLE io_open(const char *filename, int flags) + { + if (flags == IOFLAG_READ) + { +#if defined(CONF_FAMILY_WINDOWS) + // check for filename case sensitive + WIN32_FIND_DATA finddata; + + HANDLE handle; + + int length; + + length = str_length(filename); + if (!filename || !length || filename[length - 1] == '\\') + return 0x0; + handle = FindFirstFile(filename, &finddata); + if (handle == INVALID_HANDLE_VALUE + || str_comp(filename + length - str_length(finddata.cFileName), + finddata.cFileName)) + return 0x0; + FindClose(handle); +#endif + return (IOHANDLE) fopen(filename, "rb"); + } + if (flags == IOFLAG_WRITE) + return (IOHANDLE) fopen(filename, "wb"); + return 0x0; + } + + unsigned io_read(IOHANDLE io, void *buffer, unsigned size) + { + return fread(buffer, 1, size, (FILE *) io); + } + + unsigned io_skip(IOHANDLE io, int size) + { + fseek((FILE *) io, size, SEEK_CUR); + return size; + } + + int io_seek(IOHANDLE io, int offset, int origin) + { + int real_origin; + + switch (origin) + { + case IOSEEK_START: + real_origin = SEEK_SET; + break; + case IOSEEK_CUR: + real_origin = SEEK_CUR; + break; + case IOSEEK_END: + real_origin = SEEK_END; + } + + return fseek((FILE *) io, offset, origin); + } + + long int io_tell(IOHANDLE io) + { + return ftell((FILE *) io); + } + + long int io_length(IOHANDLE io) + { + long int length; + + io_seek(io, 0, IOSEEK_END); + length = io_tell(io); + io_seek(io, 0, IOSEEK_START); + return length; + } + + unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) + { + return fwrite(buffer, 1, size, (FILE *) io); + } + + int io_close(IOHANDLE io) + { + fclose((FILE *) io); + return 1; + } + + int io_flush(IOHANDLE io) + { + fflush((FILE *) io); + return 0; + } + + void *thread_create(void (*threadfunc) (void *), void *u) + { +#if defined(CONF_FAMILY_UNIX) + pthread_t id; + + pthread_create(&id, NULL, (void *(*)(void *))threadfunc, u); + return (void *)id; +#elif defined(CONF_FAMILY_WINDOWS) + return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) threadfunc, u, 0, + NULL); +#else +#error not implemented +#endif + } + + void thread_wait(void *thread) + { +#if defined(CONF_FAMILY_UNIX) + pthread_join((pthread_t) thread, NULL); +#elif defined(CONF_FAMILY_WINDOWS) + WaitForSingleObject((HANDLE) thread, INFINITE); +#else +#error not implemented +#endif + } + + void thread_destroy(void *thread) + { +#if defined(CONF_FAMILY_UNIX) + void *r = 0; + + pthread_join((pthread_t) thread, &r); +#else + /* #error not implemented */ +#endif + } + + void thread_yield() + { +#if defined(CONF_FAMILY_UNIX) + sched_yield(); +#elif defined(CONF_FAMILY_WINDOWS) + Sleep(0); +#else +#error not implemented +#endif + } + + void thread_sleep(int milliseconds) + { +#if defined(CONF_FAMILY_UNIX) + usleep(milliseconds * 1000); +#elif defined(CONF_FAMILY_WINDOWS) + Sleep(milliseconds); +#else +#error not implemented +#endif + } + + + + +#if defined(CONF_FAMILY_UNIX) + typedef pthread_mutex_t LOCKINTERNAL; +#elif defined(CONF_FAMILY_WINDOWS) + typedef CRITICAL_SECTION LOCKINTERNAL; +#else +#error not implemented on this platform +#endif + + LOCK lock_create() + { + LOCKINTERNAL *lock = + (LOCKINTERNAL *) mem_alloc(sizeof(LOCKINTERNAL), 4); + +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_init(lock, 0x0); +#elif defined(CONF_FAMILY_WINDOWS) + InitializeCriticalSection((LPCRITICAL_SECTION) lock); +#else +#error not implemented on this platform +#endif + return (LOCK) lock; + } + + void lock_destroy(LOCK lock) + { +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_destroy((LOCKINTERNAL *) lock); +#elif defined(CONF_FAMILY_WINDOWS) + DeleteCriticalSection((LPCRITICAL_SECTION) lock); +#else +#error not implemented on this platform +#endif + mem_free(lock); + } + + int lock_try(LOCK lock) + { +#if defined(CONF_FAMILY_UNIX) + return pthread_mutex_trylock((LOCKINTERNAL *) lock); +#elif defined(CONF_FAMILY_WINDOWS) + return TryEnterCriticalSection((LPCRITICAL_SECTION) lock); +#else +#error not implemented on this platform +#endif + } + + void lock_wait(LOCK lock) + { +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_lock((LOCKINTERNAL *) lock); +#elif defined(CONF_FAMILY_WINDOWS) + EnterCriticalSection((LPCRITICAL_SECTION) lock); +#else +#error not implemented on this platform +#endif + } + + void lock_release(LOCK lock) + { +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_unlock((LOCKINTERNAL *) lock); +#elif defined(CONF_FAMILY_WINDOWS) + LeaveCriticalSection((LPCRITICAL_SECTION) lock); +#else +#error not implemented on this platform +#endif + } + + /* ----- time ----- */ + int64 time_get() + { +#if defined(CONF_FAMILY_UNIX) + struct timeval val; + + gettimeofday(&val, NULL); + return (int64) val.tv_sec * (int64) 1000000 + (int64) val.tv_usec; +#elif defined(CONF_FAMILY_WINDOWS) + static int64 last = 0; + + int64 t; + + QueryPerformanceCounter((PLARGE_INTEGER) & t); + if (t < last) /* for some reason, QPC can return values in + the past */ + return last; + last = t; + return t; +#else +#error not implemented +#endif + } + + int64 time_freq() + { +#if defined(CONF_FAMILY_UNIX) + return 1000000; +#elif defined(CONF_FAMILY_WINDOWS) + int64 t; + + QueryPerformanceFrequency((PLARGE_INTEGER) & t); + return t; +#else +#error not implemented +#endif + } + + /* ----- network ----- */ + static void netaddr_to_sockaddr(const NETADDR * src, struct sockaddr *dest) + { + /* TODO: IPv6 support */ + struct sockaddr_in *p = (struct sockaddr_in *)dest; + + mem_zero(p, sizeof(struct sockaddr_in)); + p->sin_family = AF_INET; + p->sin_port = htons(src->port); + p->sin_addr.s_addr = + htonl(src->ip[0] << 24 | src->ip[1] << 16 | src->ip[2] << 8 | src-> + ip[3]); + } + + static void sockaddr_to_netaddr(const struct sockaddr *src, NETADDR * dst) + { + /* TODO: IPv6 support */ + unsigned int ip = htonl(((struct sockaddr_in *)src)->sin_addr.s_addr); + + mem_zero(dst, sizeof(NETADDR)); + dst->type = NETTYPE_IPV4; + dst->port = htons(((struct sockaddr_in *)src)->sin_port); + dst->ip[0] = (unsigned char)((ip >> 24) & 0xFF); + dst->ip[1] = (unsigned char)((ip >> 16) & 0xFF); + dst->ip[2] = (unsigned char)((ip >> 8) & 0xFF); + dst->ip[3] = (unsigned char)(ip & 0xFF); + } + + int net_addr_comp(const NETADDR * a, const NETADDR * b) + { + return mem_comp(a, b, sizeof(NETADDR)); + } + + void net_addr_str(const NETADDR * addr, char *string, int max_length) + { + if (addr->type == NETTYPE_IPV4) + str_format(string, max_length, "%d.%d.%d.%d:%d", addr->ip[0], + addr->ip[1], addr->ip[2], addr->ip[3], addr->port); + else if (addr->type == NETTYPE_IPV6) + { + str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", + (addr->ip[0] << 8) | addr->ip[1], + (addr->ip[2] << 8) | addr->ip[3], + (addr->ip[4] << 8) | addr->ip[5], + (addr->ip[6] << 8) | addr->ip[7], + (addr->ip[8] << 8) | addr->ip[9], + (addr->ip[10] << 8) | addr->ip[11], + (addr->ip[12] << 8) | addr->ip[13], + (addr->ip[14] << 8) | addr->ip[15], addr->port); + } + else + str_format(string, max_length, "unknown type %d", addr->type); + } + + int net_host_lookup(const char *hostname, NETADDR * addr, int types) + { + /* TODO: IPv6 support */ + struct addrinfo hints; + + struct addrinfo *result; + + int e; + + mem_zero(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + + e = getaddrinfo(hostname, NULL, &hints, &result); + if (e != 0 || !result) + return -1; + + sockaddr_to_netaddr(result->ai_addr, addr); + freeaddrinfo(result); + addr->port = 0; + return 0; + } + + static int parse_int(int *out, const char **str) + { + int i = 0; + + *out = 0; + if (**str < '0' || **str > '9') + return -1; + + i = **str - '0'; + (*str)++; + + while (1) + { + if (**str < '0' || **str > '9') + { + *out = i; + return 0; + } + + i = (i * 10) + (**str - '0'); + (*str)++; + } + + return 0; + } + + static int parse_char(char c, const char **str) + { + if (**str != c) + return -1; + (*str)++; + return 0; + } + + static int parse_uint8(unsigned char *out, const char **str) + { + int i; + + if (parse_int(&i, str) != 0) + return -1; + if (i < 0 || i > 0xff) + return -1; + *out = i; + return 0; + } + + static int parse_uint16(unsigned short *out, const char **str) + { + int i; + + if (parse_int(&i, str) != 0) + return -1; + if (i < 0 || i > 0xffff) + return -1; + *out = i; + return 0; + } + + int net_addr_from_str(NETADDR * addr, const char *string) + { + const char *str = string; + + mem_zero(addr, sizeof(NETADDR)); + + if (str[0] == '[') + { + /* TODO: ipv6 */ + } + else + { + /* ipv4 */ + if (parse_uint8(&addr->ip[0], &str)) + return -1; + if (parse_char('.', &str)) + return -1; + if (parse_uint8(&addr->ip[1], &str)) + return -1; + if (parse_char('.', &str)) + return -1; + if (parse_uint8(&addr->ip[2], &str)) + return -1; + if (parse_char('.', &str)) + return -1; + if (parse_uint8(&addr->ip[3], &str)) + return -1; + if (*str == ':') + { + str++; + if (parse_uint16(&addr->port, &str)) + return -1; + } + + addr->type = NETTYPE_IPV4; + } + + return 0; + } + + + NETSOCKET net_udp_create(NETADDR bindaddr) + { + /* TODO: IPv6 support */ + struct sockaddr addr; + + unsigned long mode = 1; + + int broadcast = 1; + + /* create socket */ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr_to_sockaddr(&bindaddr, &addr); + if (bind(sock, &addr, sizeof(addr)) != 0) + { + net_udp_close(sock); + return NETSOCKET_INVALID; + } + + /* set non-blocking */ +#if defined(CONF_FAMILY_WINDOWS) + ioctlsocket(sock, FIONBIO, &mode); +#else + ioctl(sock, FIONBIO, &mode); +#endif + + /* set boardcast */ + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, + sizeof(broadcast)); + + /* return */ + return sock; + } + + int net_udp_send(NETSOCKET sock, const NETADDR * addr, const void *data, + int size) + { + struct sockaddr sa; + + int32_t d; + + mem_zero(&sa, sizeof(sa)); + netaddr_to_sockaddr(addr, &sa); + if ((d = + sendto((int)sock, (const char *)data, size, 0, &sa, + sizeof(sa))) < 0) + { + int32_t err_num = errno; + + char char_buffer[256]; + + char addrstr[256]; + + net_addr_str(addr, addrstr, sizeof(addrstr)); + + dbg_msg("net", "sendto error %d %x", d, d); + dbg_msg("net", "\tsock = %d %x", sock, sock); + dbg_msg("net", "\tsize = %d %x", size, size); + + net_addr_str(addr, char_buffer, sizeof(char_buffer)); + dbg_msg("net", "\taddr = %s", char_buffer); + + dbg_msg("net", "\terr = '%d:%s'", err_num, strerror(err_num)); + } + else + { + network_stats.sent_bytes += size; + network_stats.sent_packets++; + } + return d; + } + + int net_udp_recv(NETSOCKET sock, NETADDR * addr, void *data, int maxsize) + { + struct sockaddr from; + + int bytes; + + socklen_t fromlen = sizeof(struct sockaddr); + + bytes = recvfrom(sock, (char *)data, maxsize, 0, &from, &fromlen); + if (bytes > 0) + { + sockaddr_to_netaddr(&from, addr); + network_stats.recv_bytes += bytes; + network_stats.recv_packets++; + return bytes; + } + else if (bytes == 0) + return 0; + return -1; /* error */ + } + + int net_udp_close(NETSOCKET sock) + { +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; + } + + NETSOCKET net_tcp_create(const NETADDR * a) + { + /* TODO: IPv6 support */ + struct sockaddr addr; + + /* create socket */ + int sock = socket(AF_INET, SOCK_STREAM, 0); + + if (sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr_to_sockaddr(a, &addr); + bind(sock, &addr, sizeof(addr)); + + /* return */ + return sock; + } + + int net_tcp_set_non_blocking(NETSOCKET sock) + { + unsigned long mode = 1; + +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, &mode); +#else + return ioctl(sock, FIONBIO, &mode); +#endif + } + + int net_tcp_set_blocking(NETSOCKET sock) + { + unsigned long mode = 0; + +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, &mode); +#else + return ioctl(sock, FIONBIO, &mode); +#endif + } + + int net_tcp_listen(NETSOCKET sock, int backlog) + { + return listen(sock, backlog); + } + + int net_tcp_accept(NETSOCKET sock, NETSOCKET * new_sock, NETADDR * a) + { + int s; + + socklen_t sockaddr_len; + + struct sockaddr addr; + + sockaddr_len = sizeof(addr); + + s = accept(sock, &addr, &sockaddr_len); + + if (s != -1) + { + sockaddr_to_netaddr(&addr, a); + *new_sock = s; + } + return s; + } + + int net_tcp_connect(NETSOCKET sock, const NETADDR * a) + { + struct sockaddr addr; + + netaddr_to_sockaddr(a, &addr); + return connect(sock, &addr, sizeof(addr)); + } + + int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR * a) + { + struct sockaddr addr; + + int res; + + netaddr_to_sockaddr(a, &addr); + net_tcp_set_non_blocking(sock); + res = connect(sock, &addr, sizeof(addr)); + net_tcp_set_blocking(sock); + + return res; + } + + int net_tcp_send(NETSOCKET sock, const void *data, int size) + { + int d; + + d = send((int)sock, (const char *)data, size, 0); + return d; + } + + int net_tcp_recv(NETSOCKET sock, void *data, int maxsize) + { + int bytes; + + bytes = recv((int)sock, (char *)data, maxsize, 0); + return bytes; + } + + int net_tcp_close(NETSOCKET sock) + { +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; + } + + int net_errno() + { + return errno; + } + + int net_would_block() + { + return net_errno() == EWOULDBLOCK; + } + + int net_init() + { +#if defined(CONF_FAMILY_WINDOWS) + WSADATA wsaData; + + int err = WSAStartup(MAKEWORD(1, 1), &wsaData); + + dbg_assert(err == 0, "network initialization failed."); + return err == 0 ? 0 : 1; +#endif + + return 0; + } + + int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, void *user) + { +#if defined(CONF_FAMILY_WINDOWS) + WIN32_FIND_DATA finddata; + + HANDLE handle; + + char buffer[1024 * 2]; + + str_format(buffer, sizeof(buffer), "%s/*", dir); + + handle = FindFirstFileA(buffer, &finddata); + + if (handle == INVALID_HANDLE_VALUE) + return 0; + + /* add all the entries */ + do + { + if (finddata.cFileName[0] != '.') + cb(finddata.cFileName, 0, user); + } + while (FindNextFileA(handle, &finddata)); + + FindClose(handle); + return 0; +#else + struct dirent *entry; + + DIR *d = opendir(dir); + + if (!d) + return 0; + + while ((entry = readdir(d)) != NULL) + cb(entry->d_name, 0, user); + + /* close the directory and return */ + closedir(d); + return 0; +#endif + } + + int fs_storage_path(const char *appname, char *path, int max) + { +#if defined(CONF_FAMILY_WINDOWS) + HRESULT r; + + char *home = getenv("APPDATA"); + + if (!home) + return -1; + _snprintf(path, max, "%s/%s", home, appname); + return 0; +#else + char *home = getenv("HOME"); + +#if !defined(CONF_PLATFORM_MACOSX) + int i; +#endif + if (!home) + return -1; + +#if defined(CONF_PLATFORM_MACOSX) + snprintf(path, max, "%s/Library/Application Support/%s", home, + appname); +#else + snprintf(path, max, "%s/.%s", home, appname); + for (i = strlen(home) + 2; path[i]; i++) + path[i] = tolower(path[i]); +#endif + + return 0; +#endif + } + + int fs_makedir(const char *path) + { +#if defined(CONF_FAMILY_WINDOWS) + if (_mkdir(path) == 0) + return 0; + if (errno == EEXIST) + return 0; + return -1; +#else + if (mkdir(path, 0755) == 0) + return 0; + if (errno == EEXIST) + return 0; + return -1; +#endif + } + + int fs_is_dir(const char *path) + { +#if defined(CONF_FAMILY_WINDOWS) + /* TODO: do this smarter */ + WIN32_FIND_DATA finddata; + + HANDLE handle; + + char buffer[1024 * 2]; + + str_format(buffer, sizeof(buffer), "%s/*", path); + + if ((handle = + FindFirstFileA(buffer, &finddata)) == INVALID_HANDLE_VALUE) + return 0; + + FindClose(handle); + return 1; +#else + struct stat sb; + + if (stat(path, &sb) == -1) + return 0; + + if (S_ISDIR(sb.st_mode)) + return 1; + else + return 0; +#endif + } + + int fs_chdir(const char *path) + { + if (fs_is_dir(path)) + { + chdir(path); + return 0; + } + else + return 1; + } + + void swap_endian(void *data, unsigned elem_size, unsigned num) + { + char *src = (char *)data; + + char *dst = src + (elem_size - 1); + + while (num) + { + unsigned n = elem_size >> 1; + + char tmp; + + while (n) + { + tmp = *src; + *src = *dst; + *dst = tmp; + + src++; + dst--; + n--; + } + + src = src + (elem_size >> 1); + dst = src + (elem_size - 1); + num--; + } + } + + int net_socket_read_wait(NETSOCKET sock, int time) + { + struct timeval tv; + + fd_set readfds; + + tv.tv_sec = 0; + tv.tv_usec = 1000 * time; + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + + /* don't care about writefds and exceptfds */ + select(sock + 1, &readfds, NULL, NULL, &tv); + if (FD_ISSET(sock, &readfds)) + return 1; + return 0; + } + + unsigned time_timestamp() + { + return time(0); + } + + void str_append(char *dst, const char *src, int dst_size) + { + int s = strlen(dst); + + int i = 0; + + while (s < dst_size) + { + dst[s] = src[i]; + if (!src[i]) /* check for null termination */ + break; + s++; + i++; + } + + dst[dst_size - 1] = 0; /* assure null termination */ + } + + void str_copy(char *dst, const char *src, int dst_size) + { + strncpy(dst, src, dst_size); + dst[dst_size - 1] = 0; /* assure null termination */ + } + + int str_length(const char *str) + { + return (int)strlen(str); + } + + void str_format(char *buffer, int buffer_size, const char *format, ...) + { +#if defined(CONF_FAMILY_WINDOWS) + va_list ap; + + va_start(ap, format); + _vsnprintf(buffer, buffer_size, format, ap); + va_end(ap); +#else + va_list ap; + + va_start(ap, format); + vsnprintf(buffer, buffer_size, format, ap); + va_end(ap); +#endif + + buffer[buffer_size - 1] = 0; /* assure null termination */ + } + + + + /* makes sure that the string only contains the characters between 32 and + 127 */ + void str_sanitize_strong(char *str_in) + { + unsigned char *str = (unsigned char *)str_in; + + while (*str) + { + *str &= 0x7f; + if (*str < 32) + *str = 32; + str++; + } + } + + /* makes sure that the string only contains the characters between 32 and + 255 */ + void str_sanitize_cc(char *str_in) + { + unsigned char *str = (unsigned char *)str_in; + + while (*str) + { + if (*str < 32) + *str = ' '; + str++; + } + } + + /* makes sure that the string only contains the characters between 32 and + 255 + \r\n\t */ + void str_sanitize(char *str_in) + { + unsigned char *str = (unsigned char *)str_in; + + while (*str) + { + if (*str < 32 && !(*str == '\r') && !(*str == '\n') + && !(*str == '\t')) + *str = ' '; + str++; + } + } + + char *str_skip_whitespaces(char *str) + { + while (*str + && (*str == ' ' || *str == '\t' || *str == '\n' + || *str == '\r')) + str++; + return str; + } + + /* case */ + int str_comp_nocase(const char *a, const char *b) + { +#if defined(CONF_FAMILY_WINDOWS) + return _stricmp(a, b); +#else + return strcasecmp(a, b); +#endif + } + + int str_comp(const char *a, const char *b) + { + return strcmp(a, b); + } + + int str_comp_num(const char *a, const char *b, const int num) + { + return strncmp(a, b, num); + } + + const char *str_find_nocase(const char *haystack, const char *needle) + { + while (*haystack) /* native implementation */ + { + const char *a = haystack; + + const char *b = needle; + + while (*a && *b && tolower(*a) == tolower(*b)) + { + a++; + b++; + } + if (!(*b)) + return haystack; + haystack++; + } + + return 0; + } + + + const char *str_find(const char *haystack, const char *needle) + { + while (*haystack) /* native implementation */ + { + const char *a = haystack; + + const char *b = needle; + + while (*a && *b && *a == *b) + { + a++; + b++; + } + if (!(*b)) + return haystack; + haystack++; + } + + return 0; + } + + void str_hex(char *dst, int dst_size, const void *data, int data_size) + { + static const char hex[] = "0123456789ABCDEF"; + + int b; + + for (b = 0; b < data_size && b < dst_size / 4 - 4; b++) + { + dst[b * 3] = hex[((const unsigned char *)data)[b] >> 4]; + dst[b * 3 + 1] = hex[((const unsigned char *)data)[b] & 0xf]; + dst[b * 3 + 2] = ' '; + dst[b * 3 + 3] = 0; + } + } + + int mem_comp(const void *a, const void *b, int size) + { + return memcmp(a, b, size); + } + + const MEMSTATS *mem_stats() + { + return &memory_stats; + } + + void net_stats(NETSTATS * stats_inout) + { + *stats_inout = network_stats; + } + + void gui_messagebox(const char *title, const char *message) + { +#if defined(CONF_PLATFORM_MACOSX) + DialogRef theItem; + + DialogItemIndex itemIndex; + + /* FIXME: really needed? can we rely on glfw? */ + /* HACK - get events without a bundle */ + ProcessSerialNumber psn; + + GetCurrentProcess(&psn); + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + /* END HACK */ + + CreateStandardAlert(kAlertStopAlert, + CFStringCreateWithCString(NULL, title, + kCFStringEncodingASCII), + CFStringCreateWithCString(NULL, message, + kCFStringEncodingASCII), + NULL, &theItem); + + RunStandardAlert(theItem, NULL, &itemIndex); +#elif defined(CONF_FAMILY_UNIX) + static char cmd[1024]; + + /* use xmessage which is available on nearly every X11 system */ + snprintf(cmd, sizeof(cmd), "xmessage -center -title '%s' '%s'", + title, message); + + system(cmd); +#elif defined(CONF_FAMILY_WINDOWS) + MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); +#else + /* this is not critical */ +#warning not implemented +#endif + } + + int str_isspace(char c) + { + return c == ' ' || c == '\n' || c == '\t'; + } + + char str_uppercase(char c) + { + if (c >= 'a' && c <= 'z') + return 'A' + (c - 'a'); + return c; + } + + int str_toint(const char *str) + { + return atoi(str); + } + float str_tofloat(const char *str) + { + return atof(str); + } + + + + static int str_utf8_isstart(char c) + { + if ((c & 0xC0) == 0x80) /* 10xxxxxx */ + return 0; + return 1; + } + + int str_utf8_rewind(const char *str, int cursor) + { + while (cursor) + { + cursor--; + if (str_utf8_isstart(*(str + cursor))) + break; + } + return cursor; + } + + int str_utf8_forward(const char *str, int cursor) + { + const char *buf = str + cursor; + + if (!buf[0]) + return cursor; + + if ((*buf & 0x80) == 0x0) /* 0xxxxxxx */ + return cursor + 1; + else if ((*buf & 0xE0) == 0xC0) /* 110xxxxx */ + { + if (!buf[1]) + return cursor + 1; + return cursor + 2; + } + else if ((*buf & 0xF0) == 0xE0) /* 1110xxxx */ + { + if (!buf[1]) + return cursor + 1; + if (!buf[2]) + return cursor + 2; + return cursor + 2; + } + else if ((*buf & 0xF8) == 0xF0) /* 11110xxx */ + { + if (!buf[1]) + return cursor + 1; + if (!buf[2]) + return cursor + 2; + if (!buf[3]) + return cursor + 3; + return cursor + 3; + } + + /* invalid */ + return cursor + 1; + } + + int str_utf8_encode(char *ptr, int chr) + { + /* encode */ + if (chr <= 0x7F) + { + ptr[0] = (char)chr; + return 1; + } + else if (chr <= 0x7FF) + { + ptr[0] = 0xC0 | ((chr >> 6) & 0x1F); + ptr[1] = 0x80 | (chr & 0x3F); + return 2; + } + else if (chr <= 0xFFFF) + { + ptr[0] = 0xE0 | ((chr >> 12) & 0x0F); + ptr[1] = 0xC0 | ((chr >> 6) & 0x3F); + ptr[2] = 0xC0 | (chr & 0x3F); + return 3; + } + else if (chr <= 0x10FFFF) + { + ptr[0] = 0xF0 | ((chr >> 18) & 0x07); + ptr[1] = 0xC0 | ((chr >> 12) & 0x3F); + ptr[2] = 0xC0 | ((chr >> 6) & 0x3F); + ptr[3] = 0xC0 | (chr & 0x3F); + return 4; + } + + return 0; + } + + int str_utf8_decode(const char **ptr) + { + const char *buf = *ptr; + + int ch = 0; + + do + { + if ((*buf & 0x80) == 0x0) /* 0xxxxxxx */ + { + ch = *buf; + buf++; + } + else if ((*buf & 0xE0) == 0xC0) /* 110xxxxx */ + { + ch = (*buf++ & 0x3F) << 6; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F); + if (ch == 0) + ch = -1; + } + else if ((*buf & 0xF0) == 0xE0) /* 1110xxxx */ + { + ch = (*buf++ & 0x1F) << 12; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F) << 6; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F); + if (ch == 0) + ch = -1; + } + else if ((*buf & 0xF8) == 0xF0) /* 11110xxx */ + { + ch = (*buf++ & 0x0F) << 18; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F) << 12; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F) << 6; + if (!(*buf)) + break; + ch += (*buf++ & 0x3F); + if (ch == 0) + ch = -1; + } + else + { + /* invalid */ + buf++; + break; + } + + *ptr = buf; + return ch; + } + while (0); + + /* out of bounds */ + *ptr = buf; + return -1; + + } + + + unsigned str_quickhash(const char *str) + { + unsigned hash = 5381; + + for (; *str; str++) + hash = ((hash << 5) + hash) + (*str); /* hash * 33 + c */ + return hash; + } + + +#if defined(__cplusplus) +} +#endif diff --git a/src/base/system.h~ b/src/base/system.h~ new file mode 100644 index 000000000..2ea0c00e8 --- /dev/null +++ b/src/base/system.h~ @@ -0,0 +1,1143 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +/* + Title: OS Abstraction +*/ + +#ifndef BASE_SYSTEM_H +#define BASE_SYSTEM_H + +#include "detect.h" + +#if defined(CONF_FAMILY_WINDOWS) + #include "stdint.h" +#endif +#if defined(CONF_FAMILY_UNIX) + #include +#endif +#if defined(CONF_PLATFORM_MACOSX) +#include + +typedef SInt8 int8_t; +typedef UInt8 uint8_t; +typedef SInt16 int16_t; +typedef UInt16 uint16_t; +typedef SInt32 int32_t; +typedef UInt32 uint32_t; +typedef SInt64 int64_t; +typedef UInt64 uint64_t; +typedef unsigned long uintptr_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Group: Debug */ +/* + Function: dbg_assert + Breaks into the debugger based on a test. + + Parameters: + test - Result of the test. + msg - Message that should be printed if the test fails. + + Remarks: + Does nothing in release version of the library. + + See Also: + +*/ +void dbg_assert(int test, const char *msg); +#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) +void dbg_assert_imp(const char *filename, int line, int test, const char *msg); + +/* + Function: dbg_break + Breaks into the debugger. + + Remarks: + Does nothing in release version of the library. + + See Also: + +*/ +void dbg_break(); + +/* + Function: dbg_msg + + Prints a debug message. + + Parameters: + sys - A string that describes what system the message belongs to + fmt - A printf styled format string. + + Remarks: + Does nothing in release version of the library. + + See Also: + +*/ +void dbg_msg(const char *sys, const char *fmt, ...); + +/* Group: Memory */ + +/* + Function: mem_alloc + Allocates memory. + + Parameters: + size - Size of the needed block. + alignment - Alignment for the block. + + Returns: + Returns a pointer to the newly allocated block. Returns a + null pointer if the memory couldn't be allocated. + + Remarks: + - Passing 0 to size will allocated the smallest amount possible + and return a unique pointer. + + See Also: + +*/ +void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); +#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) + +/* + Function: mem_free + Frees a block allocated through . + + Remarks: + - In the debug version of the library the function will assert if + a non-valid block is passed, like a null pointer or a block that + isn't allocated. + + See Also: + +*/ +void mem_free(void *block); + +/* + Function: mem_copy + Copies a a memory block. + + Parameters: + dest - Destination. + source - Source to copy. + size - Size of the block to copy. + + Remarks: + - This functions DOES NOT handles cases where source and + destination is overlapping. + + See Also: + +*/ +void mem_copy(void *dest, const void *source, unsigned size); + +/* + Function: mem_move + Copies a a memory block + + Parameters: + dest - Destination + source - Source to copy + size - Size of the block to copy + + Remarks: + - This functions handles cases where source and destination + is overlapping + + See Also: + +*/ +void mem_move(void *dest, const void *source, unsigned size); + +/* + Function: mem_zero + Sets a complete memory block to 0 + + Parameters: + block - Pointer to the block to zero out + size - Size of the block +*/ +void mem_zero(void *block, unsigned size); + +/* + Function: mem_comp + Compares two blocks of memory + + Parameters: + a - First block of data + b - Second block of data + size - Size of the data to compare + + Returns: + <0 - Block a is lesser then block b + 0 - Block a is equal to block b + >0 - Block a is greater then block b +*/ +int mem_comp(const void *a, const void *b, int size); + +/* + Function: mem_check + Validates the heap + Will trigger a assert if memory has failed. +*/ +int mem_check_imp(); +#define mem_check() dbg_assert_imp(__FILE__, __LINE__, mem_check_imp(), "Memory check failed") + +/* Group: File IO */ +enum { + IOFLAG_READ = 1, + IOFLAG_WRITE = 2, + IOFLAG_RANDOM = 4, + + IOSEEK_START = 0, + IOSEEK_CUR = 1, + IOSEEK_END = 2 +}; + +typedef struct IOINTERNAL *IOHANDLE; + +/* + Function: io_open + Opens a file. + + Parameters: + filename - File to open. + flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. + + Returns: + Returns a handle to the file on success and 0 on failure. + +*/ +IOHANDLE io_open(const char *filename, int flags); + +/* + Function: io_read + Reads data into a buffer from a file. + + Parameters: + io - Handle to the file to read data from. + buffer - Pointer to the buffer that will recive the data. + size - Number of bytes to read from the file. + + Returns: + Number of bytes read. + +*/ +unsigned io_read(IOHANDLE io, void *buffer, unsigned size); + +/* + Function: io_skip + Skips data in a file. + + Parameters: + io - Handle to the file. + size - Number of bytes to skip. + + Returns: + Number of bytes skipped. +*/ +unsigned io_skip(IOHANDLE io, int size); + +/* + Function: io_write + Writes data from a buffer to file. + + Parameters: + io - Handle to the file. + buffer - Pointer to the data that should be written. + size - Number of bytes to write. + + Returns: + Number of bytes written. +*/ +unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); + +/* + Function: io_seek + Seeks to a specified offset in the file. + + Parameters: + io - Handle to the file. + offset - Offset from pos to stop. + origin - Position to start searching from. + + Returns: + Returns 0 on success. +*/ +int io_seek(IOHANDLE io, int offset, int origin); + +/* + Function: io_tell + Gets the current position in the file. + + Parameters: + io - Handle to the file. + + Returns: + Returns the current position. -1L if an error occured. +*/ +long int io_tell(IOHANDLE io); + +/* + Function: io_length + Gets the total length of the file. Resetting cursor to the beginning + + Parameters: + io - Handle to the file. + + Returns: + Returns the total size. -1L if an error occured. +*/ +long int io_length(IOHANDLE io); + +/* + Function: io_close + Closes a file. + + Parameters: + io - Handle to the file. + + Returns: + Returns 0 on success. +*/ +int io_close(IOHANDLE io); + +/* + Function: io_flush + Empties all buffers and writes all pending data. + + Parameters: + io - Handle to the file. + + Returns: + Returns 0 on success. +*/ +int io_flush(IOHANDLE io); + + +/* + Function: io_stdin + Returns an to the standard input. +*/ +IOHANDLE io_stdin(); + +/* + Function: io_stdout + Returns an to the standard output. +*/ +IOHANDLE io_stdout(); + +/* + Function: io_stderr + Returns an to the standard error. +*/ +IOHANDLE io_stderr(); + + +/* Group: Threads */ + +/* + Function: thread_sleep + Suspends the current thread for a given period. + + Parameters: + milliseconds - Number of milliseconds to sleep. +*/ +void thread_sleep(int milliseconds); + +/* + Function: thread_create + Creates a new thread. + + Parameters: + threadfunc - Entry point for the new thread. + user - Pointer to pass to the thread. + +*/ +void *thread_create(void (*threadfunc)(void *), void *user); + +/* + Function: thread_wait + Waits for a thread to be done or destroyed. + + Parameters: + thread - Thread to wait for. +*/ +void thread_wait(void *thread); + +/* + Function: thread_destroy + Destroys a thread. + + Parameters: + thread - Thread to destroy. +*/ +void thread_destroy(void *thread); + +/* + Function: thread_yeild + Yeild the current threads execution slice. +*/ +void thread_yield(); + + +/* Group: Locks */ +typedef void* LOCK; + +LOCK lock_create(); +void lock_destroy(LOCK lock); + +int lock_try(LOCK lock); +void lock_wait(LOCK lock); +void lock_release(LOCK lock); + +/* Group: Timer */ +#ifdef __GNUC__ +/* if compiled with -pedantic-errors it will complain about long + not being a C90 thing. +*/ +__extension__ typedef long long int64; +#else +typedef long long int64; +#endif +/* + Function: time_get + Fetches a sample from a high resolution timer. + + Returns: + Current value of the timer. + + Remarks: + To know how fast the timer is ticking, see . +*/ +int64 time_get(); + +/* + Function: time_freq + Returns the frequency of the high resolution timer. + + Returns: + Returns the frequency of the high resolution timer. +*/ +int64 time_freq(); + +/* + Function: time_timestamp + Retrives the current time as a UNIX timestamp + + Returns: + The time as a UNIX timestamp +*/ +unsigned time_timestamp(); + +/* Group: Network General */ +typedef int NETSOCKET; +enum +{ + NETSOCKET_INVALID = -1, + + NETTYPE_INVALID = 0, + NETTYPE_IPV4 = 1, + NETTYPE_IPV6 = 2, + NETTYPE_ALL = ~0 +}; + +typedef struct +{ + unsigned int type; + unsigned char ip[16]; + unsigned short port; +} NETADDR; + +/* + Function: net_init + Initiates network functionallity. + + Returns: + Returns 0 on success, + + Remarks: + You must call this function before using any other network + functions. +*/ +int net_init(); + +/* + Function: net_host_lookup + Does a hostname lookup by name and fills out the passed + NETADDR struct with the recieved details. + + Returns: + 0 on success. +*/ +int net_host_lookup(const char *hostname, NETADDR *addr, int types); + +/* + Function: net_addr_comp + Compares two network addresses. + + Parameters: + a - Address to compare + b - Address to compare to. + + Returns: + <0 - Address a is lesser then address b + 0 - Address a is equal to address b + >0 - Address a is greater then address b +*/ +int net_addr_comp(const NETADDR *a, const NETADDR *b); + +/* + Function: net_addr_str + Turns a network address into a representive string. + + Parameters: + addr - Address to turn into a string. + string - Buffer to fill with the string. + max_length - Maximum size of the string. + + Remarks: + - The string will always be zero terminated + +*/ +void net_addr_str(const NETADDR *addr, char *string, int max_length); + +/* + Function: net_addr_from_str + Turns string into a network address. + + Returns: + 0 on success + + Parameters: + addr - Address to fill in. + string - String to parse. +*/ +int net_addr_from_str(NETADDR *addr, const char *string); + +/* Group: Network UDP */ + +/* + Function: net_udp_create + Creates a UDP socket and binds it to a port. + + Parameters: + bindaddr - Address to bind the socket to. + + Returns: + On success it returns an handle to the socket. On failure it + returns NETSOCKET_INVALID. +*/ +NETSOCKET net_udp_create(NETADDR bindaddr); + +/* + Function: net_udp_send + Sends a packet over an UDP socket. + + Parameters: + sock - Socket to use. + addr - Where to send the packet. + data - Pointer to the packet data to send. + size - Size of the packet. + + Returns: + On success it returns the number of bytes sent. Returns -1 + on error. +*/ +int32_t net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size); + +/* + Function: net_udp_recv + Recives a packet over an UDP socket. + + Parameters: + sock - Socket to use. + addr - Pointer to an NETADDR that will recive the address. + data - Pointer to a buffer that will recive the data. + maxsize - Maximum size to recive. + + Returns: + On success it returns the number of bytes recived. Returns -1 + on error. +*/ +int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize); + +/* + Function: net_udp_close + Closes an UDP socket. + + Parameters: + sock - Socket to close. + + Returns: + Returns 0 on success. -1 on error. +*/ +int net_udp_close(NETSOCKET sock); + + +/* Group: Network TCP */ + +/* + Function: net_tcp_create + Creates a TCP socket. + + Parameters: + bindaddr - Address to bind the socket to. + + Returns: + On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. +*/ +NETSOCKET net_tcp_create(const NETADDR *a); + +/* + Function: net_tcp_listen + Makes the socket start listening for new connections. + + Parameters: + sock - Socket to start listen to. + backlog - Size of the queue of incomming connections to keep. + + Returns: + Returns 0 on success. +*/ +int net_tcp_listen(NETSOCKET sock, int backlog); + +/* + Function: net_tcp_accept + Polls a listning socket for a new connection. + + Parameters: + sock - Listning socket to poll. + new_sock - Pointer to a socket to fill in with the new socket. + addr - Pointer to an address that will be filled in the remote address (optional, can be NULL). + + Returns: + Returns a non-negative integer on success. Negative integer on failure. +*/ +int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *addr); + +/* + Function: net_tcp_connect + Connects one socket to another. + + Parameters: + sock - Socket to connect. + addr - Address to connect to. + + Returns: + Returns 0 on success. + +*/ +int net_tcp_connect(NETSOCKET sock, const NETADDR *addr); + +/* + Function: net_tcp_send + Sends data to a TCP stream. + + Parameters: + sock - Socket to send data to. + data - Pointer to the data to send. + size - Size of the data to send. + + Returns: + Number of bytes sent. Negative value on failure. +*/ +int net_tcp_send(NETSOCKET sock, const void *data, int size); + +/* + Function: net_tcp_recv + Recvives data from a TCP stream. + + Parameters: + sock - Socket to recvive data from. + data - Pointer to a buffer to write the data to + max_size - Maximum of data to write to the buffer. + + Returns: + Number of bytes recvived. Negative value on failure. When in + non-blocking mode, it returns 0 when there is no more data to + be fetched. +*/ +int net_tcp_recv(NETSOCKET sock, void *data, int maxsize); + +/* + Function: net_tcp_close + Closes a TCP socket. + + Parameters: + sock - Socket to close. + + Returns: + Returns 0 on success. Negative value on failure. +*/ +int net_tcp_close(NETSOCKET sock); + +/* Group: Strings */ + +/* + Function: str_append + Appends a string to another. + + Parameters: + dst - Pointer to a buffer that contains a string. + src - String to append. + dst_size - Size of the buffer of the dst string. + + Remarks: + - The strings are treated as zero-termineted strings. + - Garantees that dst string will contain zero-termination. +*/ +void str_append(char *dst, const char *src, int dst_size); + +/* + Function: str_copy + Copies a string to another. + + Parameters: + dst - Pointer to a buffer that shall recive the string. + src - String to be copied. + dst_size - Size of the buffer dst. + + Remarks: + - The strings are treated as zero-termineted strings. + - Garantees that dst string will contain zero-termination. +*/ +void str_copy(char *dst, const char *src, int dst_size); + +/* + Function: str_length + Returns the length of a zero terminated string. + + Parameters: + str - Pointer to the string. + + Returns: + Length of string in bytes excluding the zero termination. +*/ +int str_length(const char *str); + +/* + Function: str_format + Performs printf formating into a buffer. + + Parameters: + buffer - Pointer to the buffer to recive the formated string. + buffer_size - Size of the buffer. + format - printf formating string. + ... - Parameters for the formating. + + Remarks: + - See the C manual for syntax for the printf formating string. + - The strings are treated as zero-termineted strings. + - Garantees that dst string will contain zero-termination. +*/ +void str_format(char *buffer, int buffer_size, const char *format, ...); + +/* + Function: str_sanitize_strong + Replaces all characters below 32 and above 127 with whitespace. + + Parameters: + str - String to sanitize. + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +void str_sanitize_strong(char *str); + +/* + Function: str_sanitize_cc + Replaces all characters below 32 with whitespace. + + Parameters: + str - String to sanitize. + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +void str_sanitize_cc(char *str); + +/* + Function: str_sanitize + Replaces all characters below 32 with whitespace with + exception to \t, \n and \r. + + Parameters: + str - String to sanitize. + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +void str_sanitize(char *str); + +/* + Function: str_skip_whitespaces + Skips leading whitespace characters(' ', '\t', '\n', '\r'). + + Parameters: + str - Pointer to the string. + + Returns: + Pointer to the first non-whitespace character found + within the string. + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +char *str_skip_whitespaces(char *str); + +/* + Function: str_comp_nocase + Compares to strings case insensitive. + + Parameters: + a - String to compare. + b - String to compare. + + Returns: + <0 - String a is lesser then string b + 0 - String a is equal to string b + >0 - String a is greater then string b + + Remarks: + - Only garanted to work with a-z/A-Z. + - The strings are treated as zero-termineted strings. +*/ +int str_comp_nocase(const char *a, const char *b); + + +/* + Function: str_comp_nocase + Compares to strings case sensitive. + + Parameters: + a - String to compare. + b - String to compare. + + Returns: + <0 - String a is lesser then string b + 0 - String a is equal to string b + >0 - String a is greater then string b + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +int str_comp(const char *a, const char *b); + +/* + Function: str_comp_nocase + Compares up to num characters of two strings case sensitive. + + Parameters: + a - String to compare. + b - String to compare. + num - Maximum characters to compare + + Returns: + <0 - String a is lesser then string b + 0 - String a is equal to string b + >0 - String a is greater then string b + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +int str_comp_num(const char *a, const char *b, const int num); + +/* + Function: str_find_nocase + Finds a string inside another string case insensitive. + + Parameters: + haystack - String to search in + needle - String to search for + + Returns: + A pointer into haystack where the needle was found. + Returns NULL of needle could not be found. + + Remarks: + - Only garanted to work with a-z/A-Z. + - The strings are treated as zero-termineted strings. +*/ +const char *str_find_nocase(const char *haystack, const char *needle); + +/* + Function: str_find + Finds a string inside another string case sensitive. + + Parameters: + haystack - String to search in + needle - String to search for + + Returns: + A pointer into haystack where the needle was found. + Returns NULL of needle could not be found. + + Remarks: + - The strings are treated as zero-termineted strings. +*/ +const char *str_find(const char *haystack, const char *needle); + +/* + Function: str_hex + Takes a datablock and generates a hexstring of it. + + Parameters: + dst - Buffer to fill with hex data + dst_size - size of the buffer + data - Data to turn into hex + data - Size of the data + + Remarks: + - The desination buffer will be zero-terminated +*/ +void str_hex(char *dst, int dst_size, const void *data, int data_size); + +/* Group: Filesystem */ + +/* + Function: fs_listdir + Lists the files in a directory + + Parameters: + dir - Directory to list + cb - Callback function to call for each entry + user - Pointer to give to the callback + + Returns: + Always returns 0. +*/ +typedef void (*FS_LISTDIR_CALLBACK)(const char *name, int is_dir, void *user); +int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, void *user); + +/* + Function: fs_makedir + Creates a directory + + Parameters: + path - Directory to create + + Returns: + Returns 0 on success. Negative value on failure. + + Remarks: + Does not create several directories if needed. "a/b/c" will result + in a failure if b or a does not exist. +*/ +int fs_makedir(const char *path); + +/* + Function: fs_storage_path + Fetches per user configuration directory. + + Returns: + Returns 0 on success. Negative value on failure. + + Remarks: + - Returns ~/.appname on UNIX based systems + - Returns ~/Library/Applications Support/appname on Mac OS X + - Returns %APPDATA%/Appname on Windows based systems +*/ +int fs_storage_path(const char *appname, char *path, int max); + +/* + Function: fs_is_dir + Checks if directory exists + + Returns: + Returns 1 on success, 0 on failure. +*/ +int fs_is_dir(const char *path); + +/* + Function: fs_chdir + Changes current working directory + + Returns: + Returns 0 on success, 1 on failure. +*/ +int fs_chdir(const char *path); + +/* + Group: Undocumented +*/ + + +/* + Function: net_tcp_connect_non_blocking + + DOCTODO: serp +*/ +int net_tcp_connect_non_blocking(NETSOCKET sock, const NETADDR *a); + +/* + Function: net_tcp_set_non_blocking + + DOCTODO: serp +*/ +int net_tcp_set_non_blocking(NETSOCKET sock); + +/* + Function: net_tcp_set_non_blocking + + DOCTODO: serp +*/ +int net_tcp_set_blocking(NETSOCKET sock); + +/* + Function: net_errno + + DOCTODO: serp +*/ +int net_errno(); + +/* + Function: net_would_block + + DOCTODO: serp +*/ +int net_would_block(); + +int net_socket_read_wait(NETSOCKET sock, int time); + +void mem_debug_dump(); + +void swap_endian(void *data, unsigned elem_size, unsigned num); + +/* Group: Debug levels */ +//by format +enum { + DBG_FMT_RAW = 1, //raw output + DBG_FMT_TIME = 2, //show time + DBG_FMT_SYS = 3, //show sys + DBG_FMT_FULL = 4 //show both +}; + +enum { + DBG_LEVEL_IMPORTANT = 0, //important always showed messages + DBG_LEVEL_ERROR = 1, //error messages + DBG_LEVEL_WARNING = 2, //warning messages + DBG_LEVEL_MSG = 3, //extra debug messages + DBG_LEVEL_INFO = 4 //info messages +}; + +#define DBG_LEVEL_LOW DBG_LEVEL_IMPORTANT +#define DBG_LEVEL_HIGH DBG_LEVEL_INFO + +typedef void (*DBG_LOGGER)(const char *line); +void dbg_logger(DBG_LOGGER logger); + +void dbg_logger_stdout(); +void dbg_logger_debugger(); +void dbg_logger_file(const char *filename); + +typedef struct +{ + int allocated; + int active_allocations; + int total_allocations; +} MEMSTATS; + +const MEMSTATS *mem_stats(); + +typedef struct +{ + int sent_packets; + int sent_bytes; + int recv_packets; + int recv_bytes; +} NETSTATS; + + +void net_stats(NETSTATS *stats); + +int str_toint(const char *str); +float str_tofloat(const char *str); +int str_isspace(char c); +char str_uppercase(char c); +unsigned str_quickhash(const char *str); + +/* + Function: gui_messagebox + Display plain OS-dependent message box + + Parameters: + title - title of the message box + message - text to display +*/ +void gui_messagebox(const char *title, const char *message); + + +/* + Function: str_utf8_rewind + Moves a cursor backwards in an utf8 string + + Parameters: + str - utf8 string + cursor - position in the string + + Returns: + New cursor position. + + Remarks: + - Won't move the cursor less then 0 +*/ +int str_utf8_rewind(const char *str, int cursor); + +/* + Function: str_utf8_forward + Moves a cursor forwards in an utf8 string + + Parameters: + str - utf8 string + cursor - position in the string + + Returns: + New cursor position. + + Remarks: + - Won't move the cursor beyond the zero termination marker +*/ +int str_utf8_forward(const char *str, int cursor); + +/* + Function: str_utf8_decode + Decodes an utf8 character + + Parameters: + ptr - pointer to an utf8 string. this pointer will be moved forward + + Returns: + Unicode value for the character. -1 for invalid characters and 0 for end of string. + + Remarks: + - This function will also move the pointer forward. +*/ +int str_utf8_decode(const char **ptr); + +/* + Function: str_utf8_encode + Encode an utf8 character + + Parameters: + ptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes. + + Returns: + Number of bytes put into the buffer. + + Remarks: + - Does not do zero termination of the string. +*/ +int str_utf8_encode(char *ptr, int chr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/game/client/gameclient.cpp~ b/src/game/client/gameclient.cpp~ new file mode 100644 index 000000000..a84826a3c --- /dev/null +++ b/src/game/client/gameclient.cpp~ @@ -0,0 +1,1124 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "render.h" + +#include "gameclient.h" + +#include "components/binds.h" +#include "components/broadcast.h" +#include "components/camera.h" +#include "components/chat.h" +#include "components/console.h" +#include "components/controls.h" +#include "components/damageind.h" +#include "components/debughud.h" +#include "components/effects.h" +#include "components/emoticon.h" +#include "components/flow.h" +#include "components/hud.h" +#include "components/items.h" +#include "components/killmessages.h" +#include "components/mapimages.h" +#include "components/maplayers.h" +#include "components/menus.h" +#include "components/motd.h" +#include "components/particles.h" +#include "components/players.h" +#include "components/nameplates.h" +#include "components/scoreboard.h" +#include "components/skins.h" +#include "components/sounds.h" +#include "components/voting.h" +#include "components/race_demo.h" + +CGameClient g_GameClient; + +// instanciate all systems +static CKillMessages gs_KillMessages; +static CCamera gs_Camera; +static CChat gs_Chat; +static CMotd gs_Motd; +static CBroadcast gs_Broadcast; +static CGameConsole gs_GameConsole; +static CBinds gs_Binds; +static CParticles gs_Particles; +static CMenus gs_Menus; +static CSkins gs_Skins; +static CFlow gs_Flow; +static CHud gs_Hud; +static CDebugHud gs_DebugHud; +static CControls gs_Controls; +static CEffects gs_Effects; +static CScoreboard gs_Scoreboard; +static CSounds gs_Sounds; +static CEmoticon gs_Emoticon; +static CDamageInd gsDamageInd; +static CVoting gs_Voting; +static CRaceDemo gs_RaceDemo; + +static CPlayers gs_Players; +static CNamePlates gs_NamePlates; +static CItems gs_Items; +static CMapImages gs_MapImages; + +static CMapLayers gs_MapLayersBackGround(CMapLayers::TYPE_BACKGROUND); +static CMapLayers gs_MapLayersForeGround(CMapLayers::TYPE_FOREGROUND); + +CGameClient::CStack::CStack() { m_Num = 0; } +void CGameClient::CStack::Add(class CComponent *pComponent) { m_paComponents[m_Num++] = pComponent; } + +static int gs_LoadCurrent; +static int gs_LoadTotal; + +/*static void load_sounds_thread(void *do_render) +{ + // load sounds + for(int s = 0; s < data->num_sounds; s++) + { + if(do_render) + gameclient.menus->render_loading(load_current/(float)load_total); + for(int i = 0; i < data->sounds[s].num_sounds; i++) + { + int id = Sound()->LoadWV(data->sounds[s].sounds[i].filename); + data->sounds[s].sounds[i].id = id; + } + + if(do_render) + load_current++; + } +}*/ + + + +static void ConServerDummy(IConsole::IResult *pResult, void *pUserData, int ClientID) +{ + dbg_msg("client", "this command is not available on the client"); +} + + +#include + +const char *CGameClient::Version() { return GAME_VERSION; } +const char *CGameClient::NetVersion() { return GAME_NETVERSION; } +const char *CGameClient::GetItemName(int Type) { return m_NetObjHandler.GetObjName(Type); } + +void CGameClient::OnConsoleInit() +{ + m_pClient = Kernel()->RequestInterface(); + m_pGraphics = Kernel()->RequestInterface(); + m_pTextRender = Kernel()->RequestInterface(); + m_pSound = Kernel()->RequestInterface(); + m_pInput = Kernel()->RequestInterface(); + m_pConsole = Kernel()->RequestInterface(); + m_pStorage = Kernel()->RequestInterface(); + m_pDemoPlayer = Kernel()->RequestInterface(); + m_pDemoRecorder = Kernel()->RequestInterface(); + m_pServerBrowser = Kernel()->RequestInterface(); + + // setup pointers + m_pBinds = &::gs_Binds; + m_pGameConsole = &::gs_GameConsole; + m_pParticles = &::gs_Particles; + m_pMenus = &::gs_Menus; + m_pSkins = &::gs_Skins; + m_pChat = &::gs_Chat; + m_pFlow = &::gs_Flow; + m_pCamera = &::gs_Camera; + m_pControls = &::gs_Controls; + m_pEffects = &::gs_Effects; + m_pSounds = &::gs_Sounds; + m_pMotd = &::gs_Motd; + m_pDamageind = &::gsDamageInd; + m_pMapimages = &::gs_MapImages; + m_pVoting = &::gs_Voting; + m_pRaceDemo = &::gs_RaceDemo; + m_pScoreboard = &::gs_Scoreboard; + + // make a list of all the systems, make sure to add them in the corrent render order + m_All.Add(m_pSkins); + m_All.Add(m_pMapimages); + m_All.Add(m_pEffects); // doesn't render anything, just updates effects + m_All.Add(m_pParticles); + m_All.Add(m_pBinds); + m_All.Add(m_pControls); + m_All.Add(m_pCamera); + m_All.Add(m_pSounds); + m_All.Add(m_pVoting); + m_All.Add(m_pParticles); // doesn't render anything, just updates all the particles + m_All.Add(m_pRaceDemo); + + m_All.Add(&gs_MapLayersBackGround); // first to render + m_All.Add(&m_pParticles->m_RenderTrail); + m_All.Add(&m_pParticles->m_RenderExplosions); + m_All.Add(&gs_Items); + m_All.Add(&gs_Players); + m_All.Add(&gs_MapLayersForeGround); + m_All.Add(&gs_NamePlates); + m_All.Add(&m_pParticles->m_RenderGeneral); + m_All.Add(m_pDamageind); + m_All.Add(&gs_Hud); + m_All.Add(&gs_Emoticon); + m_All.Add(&gs_KillMessages); + m_All.Add(m_pChat); + m_All.Add(&gs_Broadcast); + m_All.Add(&gs_DebugHud); + m_All.Add(&gs_Scoreboard); + m_All.Add(m_pMotd); + m_All.Add(m_pMenus); + m_All.Add(m_pGameConsole); + + // build the input stack + m_Input.Add(&m_pMenus->m_Binder); // this will take over all input when we want to bind a key + m_Input.Add(&m_pBinds->m_SpecialBinds); + m_Input.Add(m_pGameConsole); + m_Input.Add(m_pChat); // chat has higher prio due to tha you can quit it by pressing esc + m_Input.Add(m_pMotd); // for pressing esc to remove it + m_Input.Add(m_pMenus); + m_Input.Add(&gs_Emoticon); + m_Input.Add(m_pControls); + m_Input.Add(m_pBinds); + + // add the some console commands + Console()->Register("team", "i", CFGFLAG_CLIENT, ConTeam, this, "Switch team", 0); + Console()->Register("kill", "", CFGFLAG_CLIENT, ConKill, this, "Kill yourself", 0); + + // register server dummy commands for tab completion + + Console()->Register("tune", "si", CFGFLAG_SERVER, ConServerDummy, 0, "Tune variable to value", 0); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConServerDummy, 0, "Reset tuning", 0); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConServerDummy, 0, "Dump tuning", 0); + Console()->Register("change_map", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Change map", 0); + Console()->Register("restart", "?i", CFGFLAG_SERVER, ConServerDummy, 0, "Restart in x seconds", 0); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Broadcast message", 0); + //MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_serverdummy, 0); + Console()->Register("set_team", "ii", CFGFLAG_SERVER, ConServerDummy, 0, "Set team of player to team", 0); + Console()->Register("addvote", "r", CFGFLAG_SERVER, ConServerDummy, 0, "Add a voting option", 0); + //MACRO_REGISTER_COMMAND("vote", "", CFGFLAG_SERVER, con_serverdummy, 0); + Console()->Register("map_hack", "", CFGFLAG_CLIENT, ConMapHack, 0, "Tune variable to value", 0); + + Console()->Register("tune", "si", CFGFLAG_SERVER, 0, 0, "Tune variable to value", 0); + Console()->Register("tune_reset", "", CFGFLAG_SERVER, 0, 0, "Reset tuning", 0); + Console()->Register("tune_dump", "", CFGFLAG_SERVER, 0, 0, "Dump tuning", 0); + Console()->Register("change_map", "r", CFGFLAG_SERVER, 0, 0, "Change map", 0); + Console()->Register("restart", "?i", CFGFLAG_SERVER, 0, 0, "Restart in x seconds", 0); + Console()->Register("broadcast", "r", CFGFLAG_SERVER, 0, 0, "Broadcast message", 0); + Console()->Register("say", "r", CFGFLAG_SERVER, 0, 0, "Say in chat", 0); + Console()->Register("set_team", "ii", CFGFLAG_SERVER, 0, 0, "Set team of player to team", 0); + Console()->Register("addvote", "r", CFGFLAG_SERVER, 0, 0, "Add a voting option", 0); + Console()->Register("vote", "r", CFGFLAG_SERVER, 0, 0, "Force a vote to yes/no", 0); + + + + // propagate pointers + m_UI.SetGraphics(Graphics(), TextRender()); + m_RenderTools.m_pGraphics = Graphics(); + m_RenderTools.m_pUI = UI(); + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->m_pClient = this; + + // let all the other components register their console commands + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnConsoleInit(); + + + // + Console()->Chain("player_name", ConchainSpecialInfoupdate, this); + Console()->Chain("player_use_custom_color", ConchainSpecialInfoupdate, this); + Console()->Chain("player_color_body", ConchainSpecialInfoupdate, this); + Console()->Chain("player_color_feet", ConchainSpecialInfoupdate, this); + Console()->Chain("player_skin", ConchainSpecialInfoupdate, this); + + // + m_SuppressEvents = false; +} + +void CGameClient::OnInit() +{ + //m_pServerBrowser = Kernel()->RequestInterface(); + + // set the language + g_Localization.Load(g_Config.m_ClLanguagefile, Console()); + + // init all components + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnInit(); + + // setup item sizes + for(int i = 0; i < NUM_NETOBJTYPES; i++) + Client()->SnapSetStaticsize(i, m_NetObjHandler.GetObjSize(i)); + + int64 Start = time_get(); + + // load default font + static CFont *pDefaultFont; + //default_font = gfx_font_load("data/fonts/sazanami-gothic.ttf"); + pDefaultFont = TextRender()->LoadFont("data/fonts/vera.ttf"); + TextRender()->SetDefaultFont(pDefaultFont); + + g_Config.m_ClThreadsoundloading = 0; + + // setup load amount + gs_LoadTotal = g_pData->m_NumImages; + gs_LoadCurrent = 0; + if(!g_Config.m_ClThreadsoundloading) + gs_LoadTotal += g_pData->m_NumSounds; + + // load textures + for(int i = 0; i < g_pData->m_NumImages; i++) + { + g_GameClient.m_pMenus->RenderLoading(gs_LoadCurrent/gs_LoadTotal); + g_pData->m_aImages[i].m_Id = Graphics()->LoadTexture(g_pData->m_aImages[i].m_pFilename, CImageInfo::FORMAT_AUTO, 0); + gs_LoadCurrent++; + } + + ::gs_Skins.Init(); + + + // TODO: Refactor: fix threaded loading of sounds again + // load sounds + { + bool DoRender = true; + for(int s = 0; s < g_pData->m_NumSounds; s++) + { + if(DoRender) + g_GameClient.m_pMenus->RenderLoading(gs_LoadCurrent/(float)gs_LoadTotal); + for(int i = 0; i < g_pData->m_aSounds[s].m_NumSounds; i++) + { + int id = Sound()->LoadWV(g_pData->m_aSounds[s].m_aSounds[i].m_pFilename); + g_pData->m_aSounds[s].m_aSounds[i].m_Id = id; + } + + if(DoRender) + gs_LoadCurrent++; + } + } + + /*if(config.cl_threadsoundloading) + thread_create(load_sounds_thread, 0); + else + load_sounds_thread((void*)1);*/ + + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnReset(); + + int64 End = time_get(); + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "initialisation finished after %f.2ms", ((End-Start)*1000)/(float)time_freq()); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "gameclient", aBuf); + + m_ServerMode = SERVERMODE_PURE; + + m_IsRace = false; + m_RaceMsgSent = false; + m_ShowOthers = -1; + m_FlagPos = vec2(-1, -1); +} + +void CGameClient::DispatchInput() +{ + // handle mouse movement + int x=0, y=0; + Input()->MouseRelative(&x, &y); + if(x || y) + { + for(int h = 0; h < m_Input.m_Num; h++) + { + if(m_Input.m_paComponents[h]->OnMouseMove(x, y)) + break; + } + } + + // handle key presses + for(int i = 0; i < Input()->NumEvents(); i++) + { + IInput::CEvent e = Input()->GetEvent(i); + + for(int h = 0; h < m_Input.m_Num; h++) + { + if(m_Input.m_paComponents[h]->OnInput(e)) + { + //dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags); + break; + } + } + } + + // clear all events for this frame + Input()->ClearEvents(); +} + + +int CGameClient::OnSnapInput(int *pData) +{ + return m_pControls->SnapInput(pData); +} + +void CGameClient::OnConnected() +{ + m_Layers.Init(Kernel()); + m_Collision.Init(Layers()); + + RenderTools()->RenderTilemapGenerateSkip(Layers()); + + for(int i = 0; i < m_All.m_Num; i++) + { + m_All.m_paComponents[i]->OnMapLoad(); + m_All.m_paComponents[i]->OnReset(); + } + + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); + + m_ServerMode = SERVERMODE_PURE; + m_LastSendInfo = 0; + + // send the inital info + SendInfo(true); +} + +void CGameClient::OnReset() +{ + // clear out the invalid pointers + m_LastNewPredictedTick = -1; + mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap)); + + for(int i = 0; i < MAX_CLIENTS; i++) + { + m_aClients[i].m_aName[0] = 0; + m_aClients[i].m_SkinId = 0; + m_aClients[i].m_Team = 0; + m_aClients[i].m_Angle = 0; + m_aClients[i].m_Emoticon = 0; + m_aClients[i].m_EmoticonStart = -1; + m_aClients[i].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(0)->m_ColorTexture; + m_aClients[i].m_SkinInfo.m_ColorBody = vec4(1,1,1,1); + m_aClients[i].m_SkinInfo.m_ColorFeet = vec4(1,1,1,1); + m_aClients[i].m_Score = 0; + m_aClients[i].UpdateRenderInfo(); + } + + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnReset(); + + // Race + m_IsRace = false; + m_RaceMsgSent = false; + m_ShowOthers = -1; + m_FlagPos = vec2(-1, -1); +} + + +void CGameClient::UpdateLocalCharacterPos() +{ + if(g_Config.m_ClPredict && Client()->State() != IClient::STATE_DEMOPLAYBACK) + { + if(!m_Snap.m_pLocalCharacter || (m_Snap.m_pLocalCharacter->m_Health < 0) || (m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_GameOver)) + { + // don't use predicted + } + else + m_LocalCharacterPos = mix(m_PredictedPrevChar.m_Pos, m_PredictedChar.m_Pos, Client()->PredIntraGameTick()); + } + else if(m_Snap.m_pLocalCharacter && m_Snap.m_pLocalPrevCharacter) + { + m_LocalCharacterPos = mix( + vec2(m_Snap.m_pLocalPrevCharacter->m_X, m_Snap.m_pLocalPrevCharacter->m_Y), + vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y), Client()->IntraGameTick()); + } +} + + +static void Evolve(CNetObj_Character *pCharacter, int Tick) +{ + CWorldCore TempWorld; + CCharacterCore TempCore; + mem_zero(&TempCore, sizeof(TempCore)); + TempCore.Init(&TempWorld, g_GameClient.Collision());//???? + TempCore.Read(pCharacter); + + while(pCharacter->m_Tick < Tick) + { + pCharacter->m_Tick++; + TempCore.Tick(false); + TempCore.Move(); + TempCore.Quantize(); + } + + TempCore.Write(pCharacter); +} + + +void CGameClient::OnRender() +{ + /*Graphics()->Clear(1,0,0); + + menus->render_background(); + return;*/ + /* + Graphics()->Clear(1,0,0); + Graphics()->MapScreen(0,0,100,100); + + Graphics()->QuadsBegin(); + Graphics()->SetColor(1,1,1,1); + Graphics()->QuadsDraw(50, 50, 30, 30); + Graphics()->QuadsEnd(); + + return;*/ + + // update the local character position + UpdateLocalCharacterPos(); + + // dispatch all input to systems + DispatchInput(); + + // render all systems + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnRender(); + + // clear new tick flags + m_NewTick = false; + m_NewPredictedTick = false; + + // check if client info has to be resent + if(m_LastSendInfo && Client()->State() == IClient::STATE_ONLINE && !m_pMenus->IsActive() && m_LastSendInfo+time_freq()*5 < time_get()) + { + // resend if client info differs + if(str_comp(g_Config.m_PlayerName, m_aClients[m_Snap.m_LocalCid].m_aName) || + str_comp(g_Config.m_PlayerSkin, m_aClients[m_Snap.m_LocalCid].m_aSkinName) || + (g_GameClient.m_Snap.m_pGameobj && !(g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) && // no teamgame? + (g_Config.m_PlayerUseCustomColor != m_aClients[m_Snap.m_LocalCid].m_UseCustomColor || + g_Config.m_PlayerColorBody != m_aClients[m_Snap.m_LocalCid].m_ColorBody || + g_Config.m_PlayerColorFeet != m_aClients[m_Snap.m_LocalCid].m_ColorFeet))) + { + SendInfo(false); + } + m_LastSendInfo = 0; + } +} + +void CGameClient::OnMessage(int MsgId, CUnpacker *pUnpacker) +{ + + // special messages + if(MsgId == NETMSGTYPE_SV_EXTRAPROJECTILE) + { + /* + int num = msg_unpack_int(); + + for(int k = 0; k < num; k++) + { + NETOBJ_PROJECTILE proj; + for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++) + ((int *)&proj)[i] = msg_unpack_int(); + + if(msg_unpack_error()) + return; + + if(extraproj_num != MAX_EXTRA_PROJECTILES) + { + extraproj_projectiles[extraproj_num] = proj; + extraproj_num++; + } + } + + return;*/ + } + else if(MsgId == NETMSGTYPE_SV_TUNEPARAMS) + { + // unpack the new tuning + CTuningParams NewTuning; + int *pParams = (int *)&NewTuning; + for(unsigned i = 0; i < sizeof(CTuningParams)/sizeof(int); i++) + pParams[i] = pUnpacker->GetInt(); + + // check for unpacking errors + if(pUnpacker->Error()) + return; + + m_ServerMode = SERVERMODE_PURE; + + // apply new tuning + m_Tuning = NewTuning; + return; + } + + void *pRawMsg = m_NetObjHandler.SecureUnpackMsg(MsgId, pUnpacker); + if(!pRawMsg) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "dropped weird message '%s' (%d), failed on '%s'", m_NetObjHandler.GetMsgName(MsgId), MsgId, m_NetObjHandler.FailedMsgOn()); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aBuf); + return; + } + + // TODO: this should be done smarter + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnMessage(MsgId, pRawMsg); + + if(MsgId == NETMSGTYPE_SV_READYTOENTER) + { + Client()->EnterGame(); + } + else if (MsgId == NETMSGTYPE_SV_EMOTICON) + { + CNetMsg_Sv_Emoticon *pMsg = (CNetMsg_Sv_Emoticon *)pRawMsg; + + // apply + m_aClients[pMsg->m_Cid].m_Emoticon = pMsg->m_Emoticon; + m_aClients[pMsg->m_Cid].m_EmoticonStart = Client()->GameTick(); + } + else if(MsgId == NETMSGTYPE_SV_SOUNDGLOBAL) + { + if(m_SuppressEvents) + return; + + // don't enqueue pseudo-global sounds from demos (created by PlayAndRecord) + CNetMsg_Sv_SoundGlobal *pMsg = (CNetMsg_Sv_SoundGlobal *)pRawMsg; + if(pMsg->m_Soundid == SOUND_CTF_DROP || pMsg->m_Soundid == SOUND_CTF_RETURN || + pMsg->m_Soundid == SOUND_CTF_CAPTURE || pMsg->m_Soundid == SOUND_CTF_GRAB_EN || + pMsg->m_Soundid == SOUND_CTF_GRAB_PL) + g_GameClient.m_pSounds->Enqueue(pMsg->m_Soundid); + else + g_GameClient.m_pSounds->Play(CSounds::CHN_GLOBAL, pMsg->m_Soundid, 1.0f, vec2(0,0)); + } + else if(MsgId == NETMSGTYPE_SV_PLAYERTIME) + { + CNetMsg_Sv_PlayerTime *pMsg = (CNetMsg_Sv_PlayerTime *)pRawMsg; + m_aClients[pMsg->m_Cid].m_Score = (float)pMsg->m_Time/100; + } +} + +void CGameClient::OnStateChange(int NewState, int OldState) +{ + // reset everything when not already connected (to keep gathered stuff) + if(NewState < IClient::STATE_ONLINE) + OnReset(); + + // then change the state + for(int i = 0; i < m_All.m_Num; i++) + m_All.m_paComponents[i]->OnStateChange(NewState, OldState); +} + +void CGameClient::OnShutdown() {} +void CGameClient::OnEnterGame() {} + +void CGameClient::OnRconLine(const char *pLine) +{ + m_pGameConsole->PrintLine(1, pLine); +} + +void CGameClient::ProcessEvents() +{ + if(m_SuppressEvents) + return; + + int SnapType = IClient::SNAP_CURRENT; + int Num = Client()->SnapNumItems(SnapType); + for(int Index = 0; Index < Num; Index++) + { + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(SnapType, Index, &Item); + + if(Item.m_Type == NETEVENTTYPE_DAMAGEIND) + { + NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)pData; + g_GameClient.m_pEffects->DamageIndicator(vec2(ev->m_X, ev->m_Y), GetDirection(ev->m_Angle)); + } + else if(Item.m_Type == NETEVENTTYPE_EXPLOSION) + { + NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)pData; + g_GameClient.m_pEffects->Explosion(vec2(ev->m_X, ev->m_Y)); + } + else if(Item.m_Type == NETEVENTTYPE_HAMMERHIT) + { + NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)pData; + g_GameClient.m_pEffects->HammerHit(vec2(ev->m_X, ev->m_Y)); + } + else if(Item.m_Type == NETEVENTTYPE_SPAWN) + { + NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)pData; + g_GameClient.m_pEffects->PlayerSpawn(vec2(ev->m_X, ev->m_Y)); + } + else if(Item.m_Type == NETEVENTTYPE_DEATH) + { + NETEVENT_DEATH *ev = (NETEVENT_DEATH *)pData; + g_GameClient.m_pEffects->PlayerDeath(vec2(ev->m_X, ev->m_Y), ev->m_ClientId); + } + else if(Item.m_Type == NETEVENTTYPE_SOUNDWORLD) + { + NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)pData; + g_GameClient.m_pSounds->Play(CSounds::CHN_WORLD, ev->m_SoundId, 1.0f, vec2(ev->m_X, ev->m_Y)); + } + } +} + +void CGameClient::OnNewSnapshot() +{ + m_NewTick = true; + + // clear out the invalid pointers + mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap)); + m_Snap.m_LocalCid = -1; + + // mark all clients offline here + bool Online[MAX_CLIENTS] = { 0 }; + + // secure snapshot + { + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int Index = 0; Index < Num; Index++) + { + IClient::CSnapItem Item; + void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, Index, &Item); + if(m_NetObjHandler.ValidateObj(Item.m_Type, pData, Item.m_DataSize) != 0) + { + if(g_Config.m_Debug) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "invalidated index=%d type=%d (%s) size=%d id=%d", Index, Item.m_Type, m_NetObjHandler.GetObjName(Item.m_Type), Item.m_DataSize, Item.m_Id); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); + } + Client()->SnapInvalidateItem(IClient::SNAP_CURRENT, Index); + } + } + } + + ProcessEvents(); + + if(g_Config.m_DbgStress) + { + if((Client()->GameTick()%100) == 0) + { + char aMessage[64]; + int MsgLen = rand()%(sizeof(aMessage)-1); + for(int i = 0; i < MsgLen; i++) + aMessage[i] = 'a'+(rand()%('z'-'a')); + aMessage[MsgLen] = 0; + + CNetMsg_Cl_Say Msg; + Msg.m_Team = rand()&1; + Msg.m_pMessage = aMessage; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + } + } + + // go trough all the items in the snapshot and gather the info we want + { + m_Snap.m_aTeamSize[0] = m_Snap.m_aTeamSize[1] = 0; + + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int i = 0; i < Num; i++) + { + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); + + if(Item.m_Type == NETOBJTYPE_CLIENTINFO) + { + const CNetObj_ClientInfo *pInfo = (const CNetObj_ClientInfo *)pData; + int Cid = Item.m_Id; + IntsToStr(&pInfo->m_Name0, 6, m_aClients[Cid].m_aName); + IntsToStr(&pInfo->m_Skin0, 6, m_aClients[Cid].m_aSkinName); + + m_aClients[Cid].m_UseCustomColor = pInfo->m_UseCustomColor; + m_aClients[Cid].m_ColorBody = pInfo->m_ColorBody; + m_aClients[Cid].m_ColorFeet = pInfo->m_ColorFeet; + + // prepare the info + if(m_aClients[Cid].m_aSkinName[0] == 'x' || m_aClients[Cid].m_aSkinName[1] == '_') + str_copy(m_aClients[Cid].m_aSkinName, "default", 64); + + m_aClients[Cid].m_SkinInfo.m_ColorBody = m_pSkins->GetColor(m_aClients[Cid].m_ColorBody); + m_aClients[Cid].m_SkinInfo.m_ColorFeet = m_pSkins->GetColor(m_aClients[Cid].m_ColorFeet); + m_aClients[Cid].m_SkinInfo.m_Size = 64; + + // find new skin + m_aClients[Cid].m_SkinId = g_GameClient.m_pSkins->Find(m_aClients[Cid].m_aSkinName); + if(m_aClients[Cid].m_SkinId < 0) + { + m_aClients[Cid].m_SkinId = g_GameClient.m_pSkins->Find("default"); + if(m_aClients[Cid].m_SkinId < 0) + m_aClients[Cid].m_SkinId = 0; + } + + if(m_aClients[Cid].m_UseCustomColor) + m_aClients[Cid].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(m_aClients[Cid].m_SkinId)->m_ColorTexture; + else + { + m_aClients[Cid].m_SkinInfo.m_Texture = g_GameClient.m_pSkins->Get(m_aClients[Cid].m_SkinId)->m_OrgTexture; + m_aClients[Cid].m_SkinInfo.m_ColorBody = vec4(1,1,1,1); + m_aClients[Cid].m_SkinInfo.m_ColorFeet = vec4(1,1,1,1); + } + + m_aClients[Cid].UpdateRenderInfo(); + g_GameClient.m_Snap.m_NumPlayers++; + + // mark Player as online + Online[Cid] = true; + } + else if(Item.m_Type == NETOBJTYPE_PLAYERINFO) + { + const CNetObj_PlayerInfo *pInfo = (const CNetObj_PlayerInfo *)pData; + + m_aClients[pInfo->m_ClientId].m_Team = pInfo->m_Team; + m_Snap.m_paPlayerInfos[pInfo->m_ClientId] = pInfo; + + if(pInfo->m_Local) + { + m_Snap.m_LocalCid = Item.m_Id; + m_Snap.m_pLocalInfo = pInfo; + + if (pInfo->m_Team == -1) + m_Snap.m_Spectate = true; + } + + // calculate team-balance + if(pInfo->m_Team != -1) + m_Snap.m_aTeamSize[pInfo->m_Team]++; + + } + else if(Item.m_Type == NETOBJTYPE_CHARACTER) + { + const void *pOld = Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_CHARACTER, Item.m_Id); + if(pOld) + { + m_Snap.m_aCharacters[Item.m_Id].m_Active = true; + m_Snap.m_aCharacters[Item.m_Id].m_Prev = *((const CNetObj_Character *)pOld); + m_Snap.m_aCharacters[Item.m_Id].m_Cur = *((const CNetObj_Character *)pData); + + if(m_Snap.m_aCharacters[Item.m_Id].m_Prev.m_Tick) + Evolve(&m_Snap.m_aCharacters[Item.m_Id].m_Prev, Client()->PrevGameTick()); + if(m_Snap.m_aCharacters[Item.m_Id].m_Cur.m_Tick) + Evolve(&m_Snap.m_aCharacters[Item.m_Id].m_Cur, Client()->GameTick()); + } + } + else if(Item.m_Type == NETOBJTYPE_GAME) + m_Snap.m_pGameobj = (CNetObj_Game *)pData; + else if(Item.m_Type == NETOBJTYPE_FLAG) + m_Snap.m_paFlags[Item.m_Id%2] = (const CNetObj_Flag *)pData; + } + } + + // setup local pointers + if(m_Snap.m_LocalCid >= 0) + { + CSnapState::CCharacterInfo *c = &m_Snap.m_aCharacters[m_Snap.m_LocalCid]; + if(c->m_Active) + { + m_Snap.m_pLocalCharacter = &c->m_Cur; + m_Snap.m_pLocalPrevCharacter = &c->m_Prev; + m_LocalCharacterPos = vec2(m_Snap.m_pLocalCharacter->m_X, m_Snap.m_pLocalCharacter->m_Y); + } + else if(Client()->SnapFindItem(IClient::SNAP_PREV, NETOBJTYPE_CHARACTER, m_Snap.m_LocalCid)) + { + // player died + m_pControls->OnPlayerDeath(); + } + } + else + m_Snap.m_Spectate = true; + + CTuningParams StandardTuning; + StandardTuning.m_PlayerCollision = 1; + StandardTuning.m_PlayerHooking = 1; + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); + if(CurrentServerInfo.m_aGameType[0] != '0') + { + if(str_comp(CurrentServerInfo.m_aGameType, "DM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "TDM") != 0 && str_comp(CurrentServerInfo.m_aGameType, "CTF") != 0) + m_ServerMode = SERVERMODE_MOD; + else if(mem_comp(&StandardTuning, &m_Tuning, sizeof(CTuningParams)) == 0) + m_ServerMode = SERVERMODE_PURE; + else + m_ServerMode = SERVERMODE_PUREMOD; + } + + // send race msg + if(m_Snap.m_pLocalInfo) + { + CServerInfo CurrentServerInfo; + Client()->GetServerInfo(&CurrentServerInfo); + if(str_find_nocase(CurrentServerInfo.m_aGameType, "race") || str_find_nocase(CurrentServerInfo.m_aGameType, "fastcap") || str_find_nocase(CurrentServerInfo.m_aGameType, "ddrace")) + { + if(!m_IsRace) + m_IsRace = true; + if(str_find_nocase(CurrentServerInfo.m_aGameType, "fastcap")) + { + m_IsFastCap = true; + + // get Flag Pos (for demo recording) + m_FlagPos = vec2(-1, -1); + int Num = Client()->SnapNumItems(IClient::SNAP_CURRENT); + for(int i = 0; i < Num; i++) + { + IClient::CSnapItem Item; + const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); + + if(Item.m_Type == NETOBJTYPE_FLAG) + { + const CNetObj_Flag *pFlag = (const CNetObj_Flag *)pData; + if(pFlag->m_CarriedBy == -2 && pFlag->m_Team != m_aClients[m_Snap.m_LocalCid].m_Team) + m_FlagPos = vec2(pFlag->m_X, pFlag->m_Y); + } + } + } + + if(!m_RaceMsgSent && m_Snap.m_pLocalInfo) + { + CNetMsg_Cl_IsRace Msg; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + m_RaceMsgSent = true; + } + + if(m_ShowOthers == -1 || (m_ShowOthers > -1 && m_ShowOthers != g_Config.m_ClShowOthers)) + { + if(m_ShowOthers == -1 && g_Config.m_ClShowOthers) + m_ShowOthers = 1; + else + { + CNetMsg_Cl_RaceShowOthers Msg; + Msg.m_Active = g_Config.m_ClShowOthers; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + + m_ShowOthers = g_Config.m_ClShowOthers; + } + } + } + } + + // reset all scores of offline players + if(m_IsRace) + { + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!Online[i]) + m_aClients[i].m_Score = 0; + } + } + + // update render info + for(int i = 0; i < MAX_CLIENTS; i++) + m_aClients[i].UpdateRenderInfo(); +} + +void CGameClient::OnPredict() +{ + // store the previous values so we can detect prediction errors + CCharacterCore BeforePrevChar = m_PredictedPrevChar; + CCharacterCore BeforeChar = m_PredictedChar; + + // we can't predict without our own id or own character + if(m_Snap.m_LocalCid == -1 || !m_Snap.m_aCharacters[m_Snap.m_LocalCid].m_Active) + return; + + // don't predict anything if we are paused + if(m_Snap.m_pGameobj && m_Snap.m_pGameobj->m_Paused) + { + if(m_Snap.m_pLocalCharacter) + m_PredictedChar.Read(m_Snap.m_pLocalCharacter); + if(m_Snap.m_pLocalPrevCharacter) + m_PredictedPrevChar.Read(m_Snap.m_pLocalPrevCharacter); + return; + } + + // repredict character + CWorldCore World; + World.m_Tuning = m_Tuning; + + // search for players + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!m_Snap.m_aCharacters[i].m_Active) + continue; + + g_GameClient.m_aClients[i].m_Predicted.Init(&World, Collision()); + World.m_apCharacters[i] = &g_GameClient.m_aClients[i].m_Predicted; + g_GameClient.m_aClients[i].m_Predicted.Read(&m_Snap.m_aCharacters[i].m_Cur); + } + + // predict + for(int Tick = Client()->GameTick()+1; Tick <= Client()->PredGameTick(); Tick++) + { + // fetch the local + if(Tick == Client()->PredGameTick() && World.m_apCharacters[m_Snap.m_LocalCid]) + m_PredictedPrevChar = *World.m_apCharacters[m_Snap.m_LocalCid]; + + // first calculate where everyone should move + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!World.m_apCharacters[c]) + continue; + + mem_zero(&World.m_apCharacters[c]->m_Input, sizeof(World.m_apCharacters[c]->m_Input)); + if(m_Snap.m_LocalCid == c) + { + // apply player input + int *pInput = Client()->GetInput(Tick); + if(pInput) + World.m_apCharacters[c]->m_Input = *((CNetObj_PlayerInput*)pInput); + World.m_apCharacters[c]->Tick(true); + } + else + World.m_apCharacters[c]->Tick(false); + + } + + // move all players and quantize their data + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!World.m_apCharacters[c]) + continue; + + World.m_apCharacters[c]->Move(); + World.m_apCharacters[c]->Quantize(); + } + + // check if we want to trigger effects + if(Tick > m_LastNewPredictedTick) + { + m_LastNewPredictedTick = Tick; + m_NewPredictedTick = true; + + if(m_Snap.m_LocalCid != -1 && World.m_apCharacters[m_Snap.m_LocalCid]) + { + vec2 Pos = World.m_apCharacters[m_Snap.m_LocalCid]->m_Pos; + int Events = World.m_apCharacters[m_Snap.m_LocalCid]->m_TriggeredEvents; + if(Events&COREEVENT_GROUND_JUMP) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, Pos); + + /*if(events&COREEVENT_AIR_JUMP) + { + GameClient.effects->air_jump(pos); + GameClient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos); + }*/ + + //if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos); + //if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos); + if(Events&COREEVENT_HOOK_ATTACH_GROUND) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, Pos); + if(Events&COREEVENT_HOOK_HIT_NOHOOK) g_GameClient.m_pSounds->PlayAndRecord(CSounds::CHN_WORLD, SOUND_HOOK_NOATTACH, 1.0f, Pos); + //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + } + } + + if(Tick == Client()->PredGameTick() && World.m_apCharacters[m_Snap.m_LocalCid]) + m_PredictedChar = *World.m_apCharacters[m_Snap.m_LocalCid]; + } + + if(g_Config.m_Debug && g_Config.m_ClPredict && m_PredictedTick == Client()->PredGameTick()) + { + CNetObj_CharacterCore Before = {0}, Now = {0}, BeforePrev = {0}, NowPrev = {0}; + BeforeChar.Write(&Before); + BeforePrevChar.Write(&BeforePrev); + m_PredictedChar.Write(&Now); + m_PredictedPrevChar.Write(&NowPrev); + + if(mem_comp(&Before, &Now, sizeof(CNetObj_CharacterCore)) != 0) + { + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", "prediction error"); + for(unsigned i = 0; i < sizeof(CNetObj_CharacterCore)/sizeof(int); i++) + if(((int *)&Before)[i] != ((int *)&Now)[i]) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), " %d %d %d (%d %d)", i, ((int *)&Before)[i], ((int *)&Now)[i], ((int *)&BeforePrev)[i], ((int *)&NowPrev)[i]); + Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client", aBuf); + } + } + } + + m_PredictedTick = Client()->PredGameTick(); +} + +void CGameClient::CClientData::UpdateRenderInfo() +{ + m_RenderInfo = m_SkinInfo; + + // force team colors + if(g_GameClient.m_Snap.m_pGameobj && g_GameClient.m_Snap.m_pGameobj->m_Flags&GAMEFLAG_TEAMS) + { + const int TeamColors[2] = {65387, 10223467}; + if(m_Team >= 0 || m_Team <= 1) + { + m_RenderInfo.m_Texture = g_GameClient.m_pSkins->Get(m_SkinId)->m_ColorTexture; + m_RenderInfo.m_ColorBody = g_GameClient.m_pSkins->GetColor(TeamColors[m_Team]); + m_RenderInfo.m_ColorFeet = g_GameClient.m_pSkins->GetColor(TeamColors[m_Team]); + } + } +} + +void CGameClient::SendSwitchTeam(int Team) +{ + CNetMsg_Cl_SetTeam Msg; + Msg.m_Team = Team; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); +} + +void CGameClient::SendInfo(bool Start) +{ + if(Start) + { + CNetMsg_Cl_StartInfo Msg; + Msg.m_pName = g_Config.m_PlayerName; + Msg.m_pSkin = g_Config.m_PlayerSkin; + Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; + Msg.m_ColorBody = g_Config.m_PlayerColorBody; + Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + } + else + { + CNetMsg_Cl_ChangeInfo Msg; + Msg.m_pName = g_Config.m_PlayerName; + Msg.m_pSkin = g_Config.m_PlayerSkin; + Msg.m_UseCustomColor = g_Config.m_PlayerUseCustomColor; + Msg.m_ColorBody = g_Config.m_PlayerColorBody; + Msg.m_ColorFeet = g_Config.m_PlayerColorFeet; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); + + // activate timer to resend the info if it gets filtered + if(!m_LastSendInfo || m_LastSendInfo+time_freq()*5 < time_get()) + m_LastSendInfo = time_get(); + } +} + +void CGameClient::SendKill(int ClientId) +{ + CNetMsg_Cl_Kill Msg; + Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); +} + +void CGameClient::ConTeam(IConsole::IResult *pResult, void *pUserData, int ClientID) +{ + ((CGameClient*)pUserData)->SendSwitchTeam(pResult->GetInteger(0)); +} + +void CGameClient::ConKill(IConsole::IResult *pResult, void *pUserData, int ClientID) +{ + ((CGameClient*)pUserData)->SendKill(-1); +} + +void CGameClient::ConMapHack(IConsole::IResult *pResult, void *pUserData, int ClientID) { + g_Config.m_GfxClearFull ^= 1; +} + +void CGameClient::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) +{ + pfnCallback(pResult, pCallbackUserData, -1); + if(pResult->NumArguments()) + ((CGameClient*)pUserData)->SendInfo(false); +} + +IGameClient *CreateGameClient() +{ + return &g_GameClient; +} diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 20ad9dfec..48b140c2a 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -78,6 +78,7 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) CGameControllerDDRace* Controller = (CGameControllerDDRace*)GameServer()->m_pController; m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision(), &Controller->m_Teams.m_Core); m_Core.m_Pos = m_Pos; + m_Core.m_Id = GetPlayer()->GetCID(); GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core; m_ReckoningTick = 0; @@ -1327,4 +1328,4 @@ void CCharacter::Snap(int SnappingClient) } Character->m_PlayerState = m_PlayerState; -} \ No newline at end of file +} diff --git a/src/game/server/entities/character.cpp~ b/src/game/server/entities/character.cpp~ new file mode 100644 index 000000000..624e4cf12 --- /dev/null +++ b/src/game/server/entities/character.cpp~ @@ -0,0 +1,1549 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "character.h" +#include "laser.h" +#include "light.h" +#include "projectile.h" + +// input count +struct CInputCount +{ + int m_Presses; + int m_Releases; +}; + +CInputCount CountInput(int Prev, int Cur) +{ + CInputCount c = { 0, 0 }; + Prev &= INPUT_STATE_MASK; + Cur &= INPUT_STATE_MASK; + int i = Prev; + + while (i != Cur) + { + i = (i + 1) & INPUT_STATE_MASK; + if (i & 1) + c.m_Presses++; + else + c.m_Releases++; + } + + return c; +} + + +MACRO_ALLOC_POOL_ID_IMPL(CCharacter, MAX_CLIENTS) + // Character, "physical" m_pPlayer's part +CCharacter::CCharacter(CGameWorld * pWorld): +CEntity(pWorld, + NETOBJTYPE_CHARACTER) +{ + m_ProximityRadius = ms_PhysSize; + m_Health = 0; + m_Armor = 0; +} + +void CCharacter::Reset() +{ + Destroy(); +} + +bool CCharacter::Spawn(CPlayer * pPlayer, vec2 Pos) +{ + m_PlayerState = PLAYERSTATE_UNKNOWN; + m_EmoteStop = -1; + m_LastAction = -1; + m_ActiveWeapon = WEAPON_GUN; + m_LastWeapon = WEAPON_HAMMER; + m_QueuedWeapon = -1; + m_pPlayer = pPlayer; + m_Pos = Pos; + m_OlderPos = Pos; + m_OldPos = Pos; + m_RaceState = RACE_NONE; + m_PrevPos = Pos; + m_Core.Reset(); + m_BroadTime = true; + m_BroadCast = true; + m_EyeEmote = true; + CGameControllerDDRace *Controller = + (CGameControllerDDRace *) GameServer()->m_pController; + Controller->m_Teams.SetCharacterTeam(m_pPlayer->GetCID(), 0); + + m_Core.Init(&GameServer()->m_World.m_Core, GameServer()->Collision()); + m_Core.m_Pos = m_Pos; + m_Core.m_Id = m_pPlayer->GetCID(); + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = &m_Core; + + m_ReckoningTick = 0; + mem_zero(&m_SendCore, sizeof(m_SendCore)); + mem_zero(&m_ReckoningCore, sizeof(m_ReckoningCore)); + + GameServer()->m_World.InsertEntity(this); + m_Alive = true; + if (m_pPlayer->m_RconFreeze) + Freeze(-1); + GameServer()->m_pController->OnCharacterSpawn(this); + + return true; +} + +void CCharacter::Destroy() +{ + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + m_Alive = false; +} + +void CCharacter::SetWeapon(int W) +{ + if (W == m_ActiveWeapon) + return; + + m_LastWeapon = m_ActiveWeapon; + m_QueuedWeapon = -1; + m_ActiveWeapon = W; + GameServer()->CreateSound(m_Pos, SOUND_WEAPON_SWITCH); + + if (m_ActiveWeapon < 0 || m_ActiveWeapon >= NUM_WEAPONS) + m_ActiveWeapon = 0; +} + +bool CCharacter::IsGrounded() +{ + if (GameServer()->Collision()-> + CheckPoint(m_Pos.x + m_ProximityRadius / 2, + m_Pos.y + m_ProximityRadius / 2 + 5)) + return true; + if (GameServer()->Collision()-> + CheckPoint(m_Pos.x - m_ProximityRadius / 2, + m_Pos.y + m_ProximityRadius / 2 + 5)) + return true; + return false; +} + + +void CCharacter::HandleNinja() +{ + if (m_ActiveWeapon != WEAPON_NINJA) + return; + + vec2 Direction = + normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); + + if ((Server()->Tick() - m_Ninja.m_ActivationTick) > + (g_pData->m_Weapons.m_Ninja.m_Duration * Server()->TickSpeed() / 1000)) + { + // time's up, return + m_aWeapons[WEAPON_NINJA].m_Got = false; + m_ActiveWeapon = m_LastWeapon; + if (m_ActiveWeapon == WEAPON_NINJA) + m_ActiveWeapon = WEAPON_GUN; + Direction = normalize(vec2(0, 0)); + SetWeapon(m_ActiveWeapon); + return; + } + + // force ninja Weapon + SetWeapon(WEAPON_NINJA); + + m_Ninja.m_CurrentMoveTime--; + + if (m_Ninja.m_CurrentMoveTime == 0) + { + // reset velocity + m_Core.m_Vel *= 0.2f; + } + + if (m_Ninja.m_CurrentMoveTime > 0) + { + // Set velocity + m_Core.m_Vel = + m_Ninja.m_ActivationDir * g_pData->m_Weapons.m_Ninja.m_Velocity; + vec2 OldPos = m_Pos; + + GameServer()->Collision()->MoveBox(&m_Core.m_Pos, &m_Core.m_Vel, + vec2(m_ProximityRadius, + m_ProximityRadius), 0.f); + + // reset velocity so the client doesn't predict stuff + m_Core.m_Vel = vec2(0.f, 0.f); + + // check if we Hit anything along the way + { + CCharacter *aEnts[64]; + + vec2 Dir = m_Pos - OldPos; + + float Radius = m_ProximityRadius * 2.0f; + + vec2 Center = OldPos + Dir * 0.5f; + + int Num = + GameServer()->m_World.FindEntities(Center, Radius, + (CEntity **) aEnts, 64, + NETOBJTYPE_CHARACTER); + + for (int i = 0; i < Num; ++i) + { + if (aEnts[i] == this) + continue; + + // make sure we haven't Hit this object before + bool bAlreadyHit = false; + + for (int j = 0; j < m_NumObjectsHit; j++) + { + if (m_apHitObjects[j] == aEnts[i]) + bAlreadyHit = true; + } + if (bAlreadyHit) + continue; + + // check so we are sufficiently close + if (distance(aEnts[i]->m_Pos, m_Pos) > + (m_ProximityRadius * 2.0f)) + continue; + + // Hit a m_pPlayer, give him damage and stuffs... + GameServer()->CreateSound(aEnts[i]->m_Pos, SOUND_NINJA_HIT); + // set his velocity to fast upward (for now) + if (m_NumObjectsHit < 10) + m_apHitObjects[m_NumObjectsHit++] = aEnts[i]; + + aEnts[i]->TakeDamage(vec2(0, 10.0f), + g_pData->m_Weapons.m_Ninja.m_pBase-> + m_Damage, m_pPlayer->GetCID(), + WEAPON_NINJA); + } + } + + return; + } + + return; +} + + +void CCharacter::DoWeaponSwitch() +{ + // make sure we can switch + if (m_ReloadTimer != 0 || m_QueuedWeapon == -1 + || m_aWeapons[WEAPON_NINJA].m_Got) + return; + + // switch Weapon + SetWeapon(m_QueuedWeapon); +} + +void CCharacter::HandleWeaponSwitch() +{ + int WantedWeapon = m_ActiveWeapon; + + if (m_QueuedWeapon != -1) + WantedWeapon = m_QueuedWeapon; + + // select Weapon + int Next = + CountInput(m_LatestPrevInput.m_NextWeapon, + m_LatestInput.m_NextWeapon).m_Presses; + int Prev = + CountInput(m_LatestPrevInput.m_PrevWeapon, + m_LatestInput.m_PrevWeapon).m_Presses; + + if (Next < 128) // make sure we only try sane stuff + { + while (Next) // Next Weapon selection + { + WantedWeapon = (WantedWeapon + 1) % NUM_WEAPONS; + if (m_aWeapons[WantedWeapon].m_Got) + Next--; + } + } + + if (Prev < 128) // make sure we only try sane stuff + { + while (Prev) // Prev Weapon selection + { + WantedWeapon = + (WantedWeapon - 1) < 0 ? NUM_WEAPONS - 1 : WantedWeapon - 1; + if (m_aWeapons[WantedWeapon].m_Got) + Prev--; + } + } + + // Direct Weapon selection + if (m_LatestInput.m_WantedWeapon) + WantedWeapon = m_Input.m_WantedWeapon - 1; + + // check for insane values + if (WantedWeapon >= 0 && WantedWeapon < NUM_WEAPONS + && WantedWeapon != m_ActiveWeapon && m_aWeapons[WantedWeapon].m_Got) + m_QueuedWeapon = WantedWeapon; + + DoWeaponSwitch(); +} + +void CCharacter::FireWeapon() +{ + if (m_ReloadTimer != 0 /* || m_FreezeTime > 0 */ ) + return; + + DoWeaponSwitch(); + vec2 Direction = + normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); + + bool FullAuto = false; + + if (m_ActiveWeapon == WEAPON_GRENADE || m_ActiveWeapon == WEAPON_SHOTGUN + || m_ActiveWeapon == WEAPON_RIFLE) + FullAuto = true; + + + // check if we gonna fire + bool WillFire = false; + + if (CountInput(m_LatestPrevInput.m_Fire, m_LatestInput.m_Fire).m_Presses) + WillFire = true; + + if ((FullAuto || m_Super) && (m_LatestInput.m_Fire & 1) + && m_aWeapons[m_ActiveWeapon].m_Ammo) + WillFire = true; + + if (!WillFire) + return; + + // check for ammo + if (!m_aWeapons[m_ActiveWeapon].m_Ammo) + { + // 125ms is a magical limit of how fast a human can click + m_ReloadTimer = 1 * Server()->TickSpeed(); + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG); + return; + } + + vec2 ProjStartPos = m_Pos + Direction * m_ProximityRadius * 0.75f; + + switch (m_ActiveWeapon) + { + case WEAPON_HAMMER: + { + // reset objects Hit + m_NumObjectsHit = 0; + GameServer()->CreateSound(m_Pos, SOUND_HAMMER_FIRE); + + if (!g_Config.m_SvHit) + break; + + CCharacter *aEnts[64]; + + int Hits = 0; + + int Num = + GameServer()->m_World.FindEntities(ProjStartPos, + m_ProximityRadius * 0.5f, + (CEntity **) aEnts, + 64, NETOBJTYPE_CHARACTER); + + for (int i = 0; i < Num; ++i) + { + CCharacter *Target = aEnts[i]; + + // for DDRace mod or any other mod, which needs hammer hits + // through the wall remove second condition + if ((Target == this || Target->Team() != this->Team()) /* || + GameServer()->Collision()->IntersectLine(ProjStartPos, + Target->m_Pos, + NULL, + NULL) */ ) + continue; + + // set his velocity to fast upward (for now) + GameServer()->CreateHammerHit(m_Pos); + aEnts[i]->TakeDamage(vec2(0.f, -1.f), + g_pData->m_Weapons.m_Hammer.m_pBase-> + m_Damage, m_pPlayer->GetCID(), + m_ActiveWeapon); + + vec2 Dir; + + if (length(Target->m_Pos - m_Pos) > 0.0f) + Dir = normalize(Target->m_Pos - m_Pos); + else + Dir = vec2(0.f, -1.f); + + Target->m_Core.m_Vel += + normalize(Dir + vec2(0.f, -1.1f)) * 10.0f * (m_HammerType + + 1); + Target->UnFreeze(); + Hits++; + } + + // if we Hit anything, we have to wait for the reload + if (Hits) + m_ReloadTimer = Server()->TickSpeed() / 3; + + } + break; + + case WEAPON_GUN: + { + CProjectile *Proj = new CProjectile(GameWorld(), + WEAPON_GUN, // Type + m_pPlayer->GetCID(), // Owner + ProjStartPos, // Pos + Direction, // Dir + (int)(Server()->TickSpeed() * GameServer()->Tuning()->m_GunLifetime), // Span + 0, // Freeze + 0, // Explosive + 0, // Force + -1, // SoundImpact + WEAPON_GUN // Weapon + ); + + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + + Proj->FillInfo(&p); + + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + + Msg.AddInt(1); + for (unsigned i = 0; i < sizeof(CNetObj_Projectile) / sizeof(int); + i++) + Msg.AddInt(((int *)&p)[i]); + + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + + GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE); + } break; + + case WEAPON_SHOTGUN: + { + new CLaser(&GameServer()->m_World, m_Pos, Direction, + GameServer()->Tuning()->m_LaserReach, + m_pPlayer->GetCID(), 1); + GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE); + /* int ShotSpread = 2; + + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + Msg.AddInt(ShotSpread*2+1); + + for(int i = -ShotSpread; i <= ShotSpread; ++i) { float + Spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f}; float a = + GetAngle(Direction); a += Spreading[i+2]; float v = + 1-(absolute(i)/(float)ShotSpread); float Speed = + mix((float)GameServer()->Tuning()->m_ShotgunSpeeddiff, 1.0f, + v); CProjectile *Proj = new CProjectile(GameWorld(), + WEAPON_SHOTGUN, m_pPlayer->GetCID(), ProjStartPos, + vec2(cosf(a), sinf(a))*Speed, + (int)(Server()->TickSpeed()*GameServer()->Tuning()->m_Shotm_GunLifetime), + 1, 0, 0, -1, WEAPON_SHOTGUN); + + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; Proj->FillInfo(&p); + + for(unsigned i = 0; i < sizeof(CNetObj_Projectile)/sizeof(int); i++) + Msg.AddInt(((int *)&p)[i]); } + + Server()->SendMsg(&Msg, 0,m_pPlayer->GetCID()); + + GameServer()->CreateSound(m_Pos, SOUND_SHOTGUN_FIRE); */ + } + break; + + case WEAPON_GRENADE: + { + CProjectile *Proj = new CProjectile(GameWorld(), + WEAPON_GRENADE, // Type + m_pPlayer->GetCID(), // Owner + ProjStartPos, // Pos + Direction, // Dir + (int)(Server()->TickSpeed() * GameServer()->Tuning()->m_GrenadeLifetime), // Span + 0, // Freeze + true, // Explosive + 0, // Force + SOUND_GRENADE_EXPLODE, // SoundImpact + WEAPON_GRENADE // Weapon + ); // SoundImpact + + // pack the Projectile and send it to the client Directly + CNetObj_Projectile p; + + Proj->FillInfo(&p); + + CMsgPacker Msg(NETMSGTYPE_SV_EXTRAPROJECTILE); + + Msg.AddInt(1); + for (unsigned i = 0; i < sizeof(CNetObj_Projectile) / sizeof(int); + i++) + Msg.AddInt(((int *)&p)[i]); + Server()->SendMsg(&Msg, 0, m_pPlayer->GetCID()); + + GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE); + } break; + + case WEAPON_RIFLE: + { + new CLaser(GameWorld(), m_Pos, Direction, + GameServer()->Tuning()->m_LaserReach, + m_pPlayer->GetCID(), 0); + // GameServer()->CreateSound(m_Pos, SOUND_RIFLE_FIRE); + } + break; + + case WEAPON_NINJA: + { + // reset Hit objects + m_NumObjectsHit = 0; + + m_AttackTick = Server()->Tick(); + m_Ninja.m_ActivationDir = Direction; + // m_Ninja.m_CurrentMoveTime = + // g_pData->m_Weapons.m_Ninja.m_Movetime * Server()->TickSpeed() / + // 1000; + m_Ninja.m_CurrentMoveTime = 10; + // GameServer()->CreateSound(m_Pos, SOUND_NINJA_FIRE); + } + break; + + } + + m_AttackTick = Server()->Tick(); + /* + if(m_aWeapons[m_ActiveWeapon].m_Ammo > 0) // -1 == unlimited + m_aWeapons[m_ActiveWeapon].m_Ammo--; */ + if (!m_ReloadTimer) + m_ReloadTimer = + g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Firedelay * + Server()->TickSpeed() / 1000; +} + +void CCharacter::HandleWeapons() +{ + // ninja + HandleNinja(); + + vec2 Direction = + normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY)); + + // check reload timer + if (m_ReloadTimer) + { + m_ReloadTimer--; + return; + } + + // fire Weapon, if wanted + FireWeapon(); + /* + // ammo regen int AmmoRegenTime = + g_pData->m_Weapons.m_aId[m_ActiveWeapon].m_Ammoregentime; + if(AmmoRegenTime) { // If equipped and not active, regen ammo? if + (m_ReloadTimer <= 0) { if (m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart + < 0) m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = Server()->Tick(); + + if ((Server()->Tick() - m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart) >= + AmmoRegenTime * Server()->TickSpeed() / 1000) { // Add some ammo + m_aWeapons[m_ActiveWeapon].m_Ammo = + min(m_aWeapons[m_ActiveWeapon].m_Ammo + 1, 10); + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } else { + m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart = -1; } } */ + return; +} + +bool CCharacter::GiveWeapon(int Weapon, int Ammo) +{ + if (m_aWeapons[Weapon].m_Ammo < g_pData->m_Weapons.m_aId[Weapon].m_Maxammo + || !m_aWeapons[Weapon].m_Got) + { + m_aWeapons[Weapon].m_Got = true; + if (!m_FreezeTime) + m_aWeapons[Weapon].m_Ammo = + min(g_pData->m_Weapons.m_aId[Weapon].m_Maxammo, Ammo); + return true; + } + return false; +} + +void CCharacter::GiveNinja() +{ + if (!m_aWeapons[WEAPON_NINJA].m_Got) + GameServer()->CreateSound(m_Pos, SOUND_PICKUP_NINJA); + m_Ninja.m_ActivationTick = Server()->Tick(); + m_aWeapons[WEAPON_NINJA].m_Got = true; + if (!m_FreezeTime) + m_aWeapons[WEAPON_NINJA].m_Ammo = -1; + m_LastWeapon = m_ActiveWeapon; + m_ActiveWeapon = WEAPON_NINJA; +} + +void CCharacter::SetEmote(int Emote, int Tick) +{ + m_EmoteType = Emote; + m_EmoteStop = Tick; +} + +void CCharacter::OnPredictedInput(CNetObj_PlayerInput * pNewInput) +{ + // check for changes + if (mem_comp(&m_Input, pNewInput, sizeof(CNetObj_PlayerInput)) != 0) + m_LastAction = Server()->Tick(); + + // copy new input + mem_copy(&m_Input, pNewInput, sizeof(m_Input)); + m_NumInputs++; + + // or are not allowed to aim in the center + if (m_Input.m_TargetX == 0 && m_Input.m_TargetY == 0) + m_Input.m_TargetY = -1; +} + +void CCharacter::OnDirectInput(CNetObj_PlayerInput * pNewInput) +{ + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); + mem_copy(&m_LatestInput, pNewInput, sizeof(m_LatestInput)); + + if (m_NumInputs > 2 && m_pPlayer->GetTeam() != -1) + { + HandleWeaponSwitch(); + FireWeapon(); + } + + mem_copy(&m_LatestPrevInput, &m_LatestInput, sizeof(m_LatestInput)); +} + +void CCharacter::OnFinish() +{ + // TODO: this ugly + float time = + (float)(Server()->Tick() - + m_StartTime) / ((float)Server()->TickSpeed()); + CPlayerData *pData = + GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); + char aBuf[128]; + + m_CpActive = -2; + str_format(aBuf, sizeof(aBuf), + "%s finished in: %d minute(s) %5.2f second(s)", + Server()->ClientName(m_pPlayer->GetCID()), (int)time / 60, + time - ((int)time / 60 * 60)); + if (!g_Config.m_SvHideScore) + GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); + else + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + + if (time - pData->m_BestTime < 0) + { + // new record \o/ + str_format(aBuf, sizeof(aBuf), "New record: %5.2f second(s) better", + time - pData->m_BestTime); + if (!g_Config.m_SvHideScore) + GameServer()->SendChatTarget(m_pPlayer->GetCID(), aBuf); + else + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf); + } + + if (!pData->m_BestTime || time < pData->m_BestTime) + { + // update the score + pData->Set(time, m_CpCurrent); + + if (str_comp_num + (Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", + 12) != 0) + GameServer()->Score()->SaveScore(m_pPlayer->GetCID(), time, this); + } + + // update server best time + if (GameServer()->m_pController->m_CurrentRecord == 0 + || time < GameServer()->m_pController->m_CurrentRecord) + { + // check for nameless + if (str_comp_num + (Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", + 12) != 0) + { + GameServer()->m_pController->m_CurrentRecord = time; + // dbg_msg("character", "Finish"); + // GetPlayer()->SendServerRecord(); + } + + } + + m_RaceState = RACE_NONE; + // set player score + if (!GameServer()->Score()->PlayerData(m_pPlayer->GetCID())->m_CurrentTime + || GameServer()->Score()->PlayerData(m_pPlayer->GetCID())-> + m_CurrentTime > time) + { + GameServer()->Score()->PlayerData(m_pPlayer->GetCID())->m_CurrentTime = + time; + + // send it to all players + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (GameServer()->m_apPlayers[i] + && GameServer()->m_apPlayers[i]->m_IsUsingRaceClient) + { + if (g_Config.m_SvHideScore || i == m_pPlayer->GetCID()) + { + CNetMsg_Sv_PlayerTime Msg; + + char aBuf[16]; + + str_format(aBuf, sizeof(aBuf), "%.0f", time * 100.0f); // damn + // ugly + // but + // the + // only + // way + // i + // know + // to + // do + // it + int TimeToSend; + + sscanf(aBuf, "%d", &TimeToSend); + Msg.m_Time = TimeToSend; + Msg.m_Cid = m_pPlayer->GetCID(); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, i); + } + } + } + } + + int TTime = 0 - (int)time; + + if (m_pPlayer->m_Score < TTime) + m_pPlayer->m_Score = TTime; + +} + +void CCharacter::OnStart() +{ + m_RaceState = RACE_STARTED; + m_StartTime = Server()->Tick(); + m_RefreshTime = Server()->Tick(); +} + +void CCharacter::OnContinue() +{ + char aBuftime[128]; + + float time = + (float)(Server()->Tick() - + m_StartTime) / ((float)Server()->TickSpeed()); + CPlayerData *pData = + GameServer()->Score()->PlayerData(m_pPlayer->GetCID()); + if (m_RaceState == RACE_STARTED) + { + int IntTime = (int)time; + + if (m_pPlayer->m_IsUsingRaceClient) + { + CNetMsg_Sv_RaceTime Msg; + + Msg.m_Time = IntTime; + Msg.m_Check = 0; + + if (m_CpActive != -1 && m_CpTick > Server()->Tick()) + { + if (pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) + { + float Diff = + (m_CpCurrent[m_CpActive] - + pData->m_aBestCpTime[m_CpActive]) * 100; + Msg.m_Check = (int)Diff; + } + } + + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, m_pPlayer->GetCID()); + } + else + { + if (m_BroadTime) + str_format(aBuftime, sizeof(aBuftime), "%dm %ds", IntTime / 60, + IntTime % 60); + else + str_format(aBuftime, sizeof(aBuftime), ""); + + if (m_CpActive != -1 && m_CpTick > Server()->Tick()) + { + if (pData->m_BestTime && pData->m_aBestCpTime[m_CpActive] != 0) + { + char aTmp[128]; + + float Diff = + m_CpCurrent[m_CpActive] - + pData->m_aBestCpTime[m_CpActive]; + str_format(aTmp, sizeof(aTmp), + "\nCheckpoint | Diff : %+5.2f", Diff); + strcat(aBuftime, aTmp); + } + } + + if (g_Config.m_SvBroadcast[0] != 0 && m_BroadCast) + { + char aTmp[128]; + + str_format(aTmp, sizeof(aTmp), "\n%s\n", + g_Config.m_SvBroadcast); + strcat(aBuftime, aTmp); + } + GameServer()->SendBroadcast(aBuftime, m_pPlayer->GetCID()); + } + } + else + { + if (g_Config.m_SvBroadcast[0] != 0) + { + char aTmp[128]; + + str_format(aTmp, sizeof(aTmp), "%s\n", g_Config.m_SvBroadcast); + strcat(aBuftime, aTmp); + GameServer()->SendBroadcast(g_Config.m_SvBroadcast, + m_pPlayer->GetCID()); + } + + } + m_RefreshTime = Server()->Tick(); +} + +void CCharacter::Tick() +{ + CGameControllerDDRace *Controller = + (CGameControllerDDRace *) GameServer()->m_pController; + int MapIndex = GameServer()->Collision()->GetMapIndex(m_PrevPos, m_Pos); + + int MapIndexL = + GameServer()->Collision()-> + GetMapIndex(vec2(m_Pos.x + m_ProximityRadius / 2 + 1, m_Pos.y), + vec2(m_Pos.x + m_ProximityRadius / 2 + 1, m_Pos.y)); + int MapIndexR = + GameServer()->Collision()-> + GetMapIndex(vec2(m_Pos.x - m_ProximityRadius / 2 + 1, m_Pos.y), + vec2(m_Pos.x - m_ProximityRadius / 2 + 1, m_Pos.y)); + int MapIndexT = + GameServer()->Collision()-> + GetMapIndex(vec2(m_Pos.x, m_Pos.y + m_ProximityRadius / 2 + 6), + vec2(m_Pos.x, m_Pos.y + m_ProximityRadius / 2 + 6)); + int MapIndexB = + GameServer()->Collision()-> + GetMapIndex(vec2(m_Pos.x, m_Pos.y - m_ProximityRadius / 2 - 4), + vec2(m_Pos.x, m_Pos.y - m_ProximityRadius / 2 - 4)); + int TileIndex = GameServer()->Collision()->GetCollisionDDRace(MapIndex); + + int TileFIndex = GameServer()->Collision()->GetFCollisionDDRace(MapIndex); + + int TileIndexL = GameServer()->Collision()->GetCollisionDDRace(MapIndexL); + + int TileFIndexL = + GameServer()->Collision()->GetFCollisionDDRace(MapIndexL); + int TileIndexR = GameServer()->Collision()->GetCollisionDDRace(MapIndexR); + + int TileFIndexR = + GameServer()->Collision()->GetFCollisionDDRace(MapIndexR); + int TileIndexT = GameServer()->Collision()->GetCollisionDDRace(MapIndexT); + + int TileFIndexT = + GameServer()->Collision()->GetFCollisionDDRace(MapIndexT); + int TileIndexB = GameServer()->Collision()->GetCollisionDDRace(MapIndexB); + + int TileFIndexB = + GameServer()->Collision()->GetFCollisionDDRace(MapIndexB); + m_CurrentTile = TileIndex; + m_CurrentFTile = TileFIndex; + + if (m_pPlayer->m_ForceBalanced) + { + char Buf[128]; + + str_format(Buf, sizeof(Buf), + "You were moved to %s due to team balancing", + GameServer()->m_pController->GetTeamName(m_pPlayer-> + GetTeam())); + GameServer()->SendBroadcast(Buf, m_pPlayer->GetCID()); + + m_pPlayer->m_ForceBalanced = false; + } + m_Armor = (m_FreezeTime != -1) ? 10 - (m_FreezeTime / 15) : 0; + if (m_Input.m_Direction != 0 || m_Input.m_Jump != 0) + m_LastMove = Server()->Tick(); + + if (m_FreezeTime > 0 || m_FreezeTime == -1) + { + if (m_FreezeTime % Server()->TickSpeed() == 0 || m_FreezeTime == -1) + { + GameServer()->CreateDamageInd(m_Pos, 0, + m_FreezeTime / + Server()->TickSpeed()); + } + if (m_FreezeTime != -1) + m_FreezeTime--; + else + m_Ninja.m_ActivationTick = Server()->Tick(); + m_Input.m_Direction = 0; + m_Input.m_Jump = 0; + m_Input.m_Hook = 0; + + // m_Input.m_Fire = 0; + if (m_FreezeTime == 1) + { + UnFreeze(); + } + } + m_Core.m_Input = m_Input; + m_Core.m_TeamBegin = Team()->m_Members.begin(); + m_Core.m_TeamEnd = Team()->m_Members.end(); + m_Core.Tick(true); + + m_DoSplash = false; + if (g_Config.m_SvEndlessDrag) + m_Core.m_HookTick = 0; + if (m_Super && m_Core.m_Jumped > 1) + m_Core.m_Jumped = 1; + if (m_Super && g_Config.m_SvEndlessSuperHook) + m_Core.m_HookTick = 0; + /* dbg_msg("character","TileIndex=%d , + TileFIndex=%d",TileIndex,TileFIndex); //REMOVE */ + // DDRace + + if (Server()->Tick() - m_RefreshTime >= Server()->TickSpeed()) + { + OnContinue(); + } + + float time = + (float)(Server()->Tick() - + m_StartTime) / ((float)Server()->TickSpeed()); + int cp = GameServer()->Collision()->IsCheckpoint(MapIndex); + + if (cp != -1 && m_RaceState == RACE_STARTED && cp > m_CpActive) + { + m_CpActive = cp; + m_CpCurrent[cp] = time; + m_CpTick = Server()->Tick() + Server()->TickSpeed() * 2; + } + if (((TileIndex == TILE_BEGIN) || (TileFIndex == TILE_BEGIN)) + && (m_RaceState == RACE_NONE + || (m_RaceState == RACE_STARTED && !Team()))) + { + Controller->m_Teams.OnCharacterStart(m_pPlayer->GetCID()); + m_CpActive = -2; + } + + if (((TileIndex == TILE_END) || (TileFIndex == TILE_END)) + && m_RaceState == RACE_STARTED) + { + Controller->m_Teams.OnCharacterFinish(m_pPlayer->GetCID()); + } + if (((TileIndex == TILE_FREEZE) || (TileFIndex == TILE_FREEZE)) + && !m_Super) + { + Freeze(Server()->TickSpeed() * 3); + } + else if ((TileIndex == TILE_UNFREEZE) || (TileFIndex == TILE_UNFREEZE)) + { + UnFreeze(); + } + if ((TileIndexT == TILE_STOPA || TileFIndexT == TILE_STOPA + || TileIndex == TILE_STOPT || TileIndexT == TILE_STOPT + || TileFIndex == TILE_STOPT || TileFIndexT == TILE_STOPT + || TileIndexT == TILE_STOPV || TileFIndexT == TILE_STOPV) + && m_Core.m_Vel.y > 0) + { + m_Core.m_Pos.y = m_PrevPos.y; + m_Core.m_Vel.y = 0; + m_Core.m_Jumped = 0; + } + if ((TileIndexB == TILE_STOPA || TileFIndexB == TILE_STOPA + || TileIndex == TILE_STOPB || TileIndexB == TILE_STOPB + || TileFIndex == TILE_STOPB || TileFIndexB == TILE_STOPB + || TileIndexB == TILE_STOPV || TileFIndexB == TILE_STOPV) + && m_Core.m_Vel.y < 0) + { + m_Core.m_Pos.y = m_PrevPos.y; + m_Core.m_Vel.y = 0; + } + if ((TileIndexR == TILE_STOPA || TileFIndexR == TILE_STOPA + || TileIndex == TILE_STOPR || TileIndexR == TILE_STOPR + || TileFIndex == TILE_STOPR || TileFIndexR == TILE_STOPR + || TileIndexR == TILE_STOPH || TileFIndexR == TILE_STOPH) + && m_Core.m_Vel.x < 0) + { + m_Core.m_Pos.x = m_PrevPos.x; + m_Core.m_Vel.x = 0; + } + if ((TileIndexL == TILE_STOPA || TileFIndexL == TILE_STOPA + || TileIndex == TILE_STOPL || TileIndexL == TILE_STOPL + || TileFIndex == TILE_STOPL || TileFIndexL == TILE_STOPL + || TileIndexL == TILE_STOPH || TileFIndexL == TILE_STOPH) + && m_Core.m_Vel.x > 0) + { + m_Core.m_Pos.x = m_PrevPos.x; + m_Core.m_Vel.x = 0; + } + if (TileIndex == TILE_BOOST_L || TileFIndex == TILE_BOOST_L) + { + if (m_PrevPos.x - m_Pos.x < 0) + m_Core.m_Vel.x += m_Core.m_Vel.x * -0.5; + else if (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex) + m_Core.m_Vel.x += m_Core.m_Vel.x * 0.5; + } + if (TileIndex == TILE_BOOST_R || TileFIndex == TILE_BOOST_R) + { + if (m_PrevPos.x - m_Pos.x > 0) + m_Core.m_Vel.x += m_Core.m_Vel.x * -0.5; + else if (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex) + m_Core.m_Vel.x += m_Core.m_Vel.x * 0.5; + } + if (TileIndex == TILE_BOOST_D || TileFIndex == TILE_BOOST_D) + { + if (m_PrevPos.y - m_Pos.y > 0) + m_Core.m_Vel.y += m_Core.m_Vel.y * -0.5; + else if (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex) + m_Core.m_Vel.y += m_Core.m_Vel.y * 0.5; + } + if (TileIndex == TILE_BOOST_U || TileFIndex == TILE_BOOST_U) + { + if (m_PrevPos.y - m_Pos.y < 0) + m_Core.m_Vel.y += m_Core.m_Vel.y * -0.5; + else if (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex) + m_Core.m_Vel.y += m_Core.m_Vel.y * 0.5; + } + if ((TileIndex == TILE_BOOST_L2 || TileFIndex == TILE_BOOST_L2) + && (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex)) + { + if (m_PrevPos.x - m_Pos.x < 0) + m_Core.m_Vel.x = m_Core.m_Vel.x * -1.1; + else + m_Core.m_Vel.x += m_Core.m_Vel.x * 1.1; + } + if ((TileIndex == TILE_BOOST_R2 || TileFIndex == TILE_BOOST_R2) + && (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex)) + { + if (m_Core.m_Vel.x < 0) + m_Core.m_Vel.x = m_Core.m_Vel.x * -1.1; + else + m_Core.m_Vel.x += m_Core.m_Vel.x * 1.1; + } + if ((TileIndex == TILE_BOOST_D2 || TileFIndex == TILE_BOOST_D2) + && (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex)) + { + if (m_PrevPos.y - m_Pos.y > 0) + m_Core.m_Vel.y = m_Core.m_Vel.y * -1.1; + else + m_Core.m_Vel.y += m_Core.m_Vel.y * 1.1; + } + if ((TileIndex == TILE_BOOST_U2 || TileFIndex == TILE_BOOST_U2) + && (m_LastBooster != TileIndex || m_LastFBooster != TileFIndex)) + { + if (m_PrevPos.y - m_Pos.y < 0) + m_Core.m_Vel.y = m_Core.m_Vel.y * -1.1; + else + m_Core.m_Vel.y += m_Core.m_Vel.y * 1.1; + } + m_LastBooster = TileIndex; + m_LastFBooster = TileFIndex; + // handle speedup tiles + if (GameServer()->Collision()-> + IsSpeedup((int)m_Core.m_Pos.x, (int)m_Core.m_Pos.y)) + { + vec2 Direction; + + int Force; + + GameServer()->Collision()->GetSpeedup((int)m_Core.m_Pos.x, + (int)m_Core.m_Pos.y, &Direction, + &Force); + + m_Core.m_Vel += Direction * Force; + } + int z = GameServer()->Collision()->IsTeleport(m_Pos.x, m_Pos.y); + + if (z) + { + m_Core.m_HookedPlayer = -1; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_TriggeredEvents |= COREEVENT_HOOK_RETRACT; + m_Core.m_HookState = HOOK_RETRACTED; + m_Core.m_Pos = + ((CGameControllerDDRace *) GameServer()->m_pController)-> + m_pTeleporter[z - 1]; + m_Core.m_HookPos = m_Core.m_Pos; + } + + // handle death-tiles + if ((GameServer()->Collision()-> + GetCollisionAt(m_Pos.x + m_ProximityRadius / 3.f, + m_Pos.y - + m_ProximityRadius / 3.f) & CCollision::COLFLAG_DEATH + || GameServer()->Collision()->GetCollisionAt(m_Pos.x + + m_ProximityRadius / 3.f, + m_Pos.y + + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetCollisionAt(m_Pos.x - + m_ProximityRadius / 3.f, + m_Pos.y - + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetCollisionAt(m_Pos.x - + m_ProximityRadius / 3.f, + m_Pos.y + + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetFCollisionAt(m_Pos.x + + m_ProximityRadius / 3.f, + m_Pos.y - + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetFCollisionAt(m_Pos.x + + m_ProximityRadius / 3.f, + m_Pos.y + + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetFCollisionAt(m_Pos.x - + m_ProximityRadius / 3.f, + m_Pos.y - + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH + || GameServer()->Collision()->GetFCollisionAt(m_Pos.x - + m_ProximityRadius / 3.f, + m_Pos.y + + m_ProximityRadius / + 3.f) & CCollision:: + COLFLAG_DEATH) && !m_Super) + { + Die(m_pPlayer->GetCID(), WEAPON_WORLD); + } + + // kill player when leaving gamelayer + if ((int)m_Pos.x / 32 < -200 + || (int)m_Pos.x / 32 > GameServer()->Collision()->GetWidth() + 200 + || (int)m_Pos.y / 32 < -200 + || (int)m_Pos.y / 32 > GameServer()->Collision()->GetHeight() + 200) + { + Die(m_pPlayer->GetCID(), WEAPON_WORLD); + } + + // handle Weapons + HandleWeapons(); + + m_PlayerState = m_Input.m_PlayerState; + + // Previnput + m_PrevInput = m_Input; + if (!m_Doored) + { + m_OlderPos = m_OldPos; + m_OldPos = m_Core.m_Pos; + } + m_PrevPos = m_Core.m_Pos; + return; +} + +float point_distance(vec2 point, vec2 line_start, vec2 line_end) +{ + float res = -1.0f; + + vec2 dir = normalize(line_end - line_start); + + for (int i = 0; i < length(line_end - line_start); i++) + { + vec2 step = dir; + + step.x *= i; + step.y *= i; + float dist = distance(step + line_start, point); + + if (res < 0 || dist < res) + res = dist; + } + return res; +} + +void CCharacter::ResetPos() +{ + m_Core.m_Pos = m_OlderPos; + // core.pos-=core.vel; + m_Core.m_Vel = vec2(0, 0); + if (m_Core.m_Jumped >= 2) + m_Core.m_Jumped = 1; +} + +void CCharacter::TickDefered() +{ + // advance the dummy + { + CWorldCore TempWorld; + + m_ReckoningCore.Init(&TempWorld, GameServer()->Collision()); + m_ReckoningCore.Tick(false); + m_ReckoningCore.Move(); + m_ReckoningCore.Quantize(); + } + + // lastsentcore + vec2 StartPos = m_Core.m_Pos; + + vec2 StartVel = m_Core.m_Vel; + + bool StuckBefore = + GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + + m_Core.Move(); + if (m_Doored) + { + ResetPos(); + m_Doored = false; + } + bool StuckAfterMove = + GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Core.Quantize(); + bool StuckAfterQuant = + GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f)); + m_Pos = m_Core.m_Pos; + + if (!StuckBefore && (StuckAfterMove || StuckAfterQuant)) + { + // Hackish solution to get rid of strict-aliasing warning + union + { + float f; + unsigned u; + } StartPosX, StartPosY, StartVelX, StartVelY; + + StartPosX.f = StartPos.x; + StartPosY.f = StartPos.y; + StartVelX.f = StartVel.x; + StartVelY.f = StartVel.y; + + char aBuf[256]; + + str_format(aBuf, sizeof(aBuf), + "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x", StuckBefore, + StuckAfterMove, StuckAfterQuant, StartPos.x, StartPos.y, + StartVel.x, StartVel.y, StartPosX.u, StartPosY.u, + StartVelX.u, StartVelY.u); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", + aBuf); + } + + int Events = m_Core.m_TriggeredEvents; + + int Mask = CmaskAllExceptOne(m_pPlayer->GetCID()); + + if (Events & COREEVENT_GROUND_JUMP) + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_JUMP, Mask); + + if (Events & COREEVENT_HOOK_ATTACH_PLAYER) + GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_PLAYER, CmaskAll()); + if (Events & COREEVENT_HOOK_ATTACH_GROUND) + GameServer()->CreateSound(m_Pos, SOUND_HOOK_ATTACH_GROUND, Mask); + if (Events & COREEVENT_HOOK_HIT_NOHOOK) + GameServer()->CreateSound(m_Pos, SOUND_HOOK_NOATTACH, Mask); + + + if (m_pPlayer->GetTeam() == -1) + { + m_Pos.x = m_Input.m_TargetX; + m_Pos.y = m_Input.m_TargetY; + } + + // update the m_SendCore if needed + { + CNetObj_Character Predicted; + + CNetObj_Character Current; + + mem_zero(&Predicted, sizeof(Predicted)); + mem_zero(&Current, sizeof(Current)); + m_ReckoningCore.Write(&Predicted); + m_Core.Write(&Current); + + // only allow dead reackoning for a top of 3 seconds + if (m_Core.m_pReset + || m_ReckoningTick + Server()->TickSpeed() * 3 < Server()->Tick() + || mem_comp(&Predicted, &Current, sizeof(CNetObj_Character)) != 0) + { + m_ReckoningTick = Server()->Tick(); + m_SendCore = m_Core; + m_ReckoningCore = m_Core; + m_Core.m_pReset = false; + } + } +} + +bool CCharacter::Freeze(int Time) +{ + if ((Time <= 1 || m_Super || m_FreezeTime == -1) && Time != -1) + return false; + if (m_FreezeTick < Server()->Tick() - Server()->TickSpeed()) + { + for (int i = 0; i < NUM_WEAPONS; i++) + if (m_aWeapons[i].m_Got) + { + m_aWeapons[i].m_Ammo = 0; + } + m_Armor = 0; + m_FreezeTime = Time; + m_FreezeTick = Server()->Tick(); + return true; + } + return false; +} + +bool CCharacter::Freeze() +{ + int Time = Server()->TickSpeed() * 3; + + if (Time <= 1 || m_Super || m_FreezeTime == -1) + return false; + if (m_FreezeTick < Server()->Tick() - Server()->TickSpeed()) + { + for (int i = 0; i < NUM_WEAPONS; i++) + if (m_aWeapons[i].m_Got) + { + m_aWeapons[i].m_Ammo = 0; + } + m_Armor = 0; + m_Ninja.m_ActivationTick = Server()->Tick(); + m_FreezeTime = Time; + m_FreezeTick = Server()->Tick(); + return true; + } + return false; +} + +bool CCharacter::UnFreeze() +{ + if (m_FreezeTime > 0) + { + m_Armor = 10; + for (int i = 0; i < NUM_WEAPONS; i++) + if (m_aWeapons[i].m_Got) + { + m_aWeapons[i].m_Ammo = -1; + } + if (!m_aWeapons[m_ActiveWeapon].m_Got) + m_ActiveWeapon = WEAPON_GUN; + m_FreezeTime = 0; + m_FreezeTick = 0; + return true; + } + return false; +} + +void CCharacter::GiveAllWeapons() +{ + for (int i = 1; i < NUM_WEAPONS - 1; i++) + { + m_aWeapons[i].m_Got = true; + if (!m_FreezeTime) + m_aWeapons[i].m_Ammo = -1; + } + return; +} + +bool CCharacter::IncreaseHealth(int Amount) +{ + if (m_Health >= 10) + return false; + m_Health = clamp(m_Health + Amount, 0, 10); + return true; +} + +bool CCharacter::IncreaseArmor(int Amount) +{ + if (m_Armor >= 10) + return false; + m_Armor = clamp(m_Armor + Amount, 0, 10); + return true; +} + +void CCharacter::Die(int Killer, int Weapon) +{ + int ModeSpecial = + GameServer()->m_pController->OnCharacterDeath(this, + GameServer()-> + m_apPlayers[Killer], + Weapon); + CGameControllerDDRace *Controller = + (CGameControllerDDRace *) GameServer()->m_pController; + char aBuf[256]; + + str_format(aBuf, sizeof(aBuf), + "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d", + Killer, Server()->ClientName(Killer), m_pPlayer->GetCID(), + Server()->ClientName(m_pPlayer->GetCID()), Weapon, ModeSpecial); + GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", aBuf); + + Controller->m_Teams.SetCharacterTeam(m_pPlayer->GetCID(), 0); + + // send the kill message + CNetMsg_Sv_KillMsg Msg; + + Msg.m_Killer = Killer; + Msg.m_Victim = m_pPlayer->GetCID(); + Msg.m_Weapon = Weapon; + Msg.m_ModeSpecial = ModeSpecial; + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + + // a nice sound + GameServer()->CreateSound(m_Pos, SOUND_PLAYER_DIE); + + // this is for auto respawn after 3 secs + m_pPlayer->m_DieTick = Server()->Tick(); + + m_Alive = false; + GameServer()->m_World.RemoveEntity(this); + GameServer()->m_World.m_Core.m_apCharacters[m_pPlayer->GetCID()] = 0; + GameServer()->CreateDeath(m_Pos, m_pPlayer->GetCID()); + + // we got to wait 0.5 secs before respawning + m_pPlayer->m_RespawnTick = Server()->Tick() + Server()->TickSpeed() / 2; +} + +bool CCharacter::TakeDamage(vec2 Force, int Dmg, int From, int Weapon) +{ + m_Core.m_Vel += Force; + /* + if(GameServer()->m_pController->IsFriendlyFire(m_pPlayer->GetCID(), + From) && !g_Config.m_SvTeamdamage) return false; + + // m_pPlayer only inflicts half damage on self if(From == + m_pPlayer->GetCID()) Dmg = max(1, Dmg/2); + + m_DamageTaken++; + + // create healthmod indicator if(Server()->Tick() < + m_DamageTakenTick+25) { // make sure that the damage indicators doesn't + group together GameServer()->CreateDamageInd(m_Pos, + m_DamageTaken*0.25f, Dmg); } else { m_DamageTaken = 0; + GameServer()->CreateDamageInd(m_Pos, 0, Dmg); } + + if(Dmg) { if(m_Armor) { if(Dmg > 1) { m_Health--; Dmg--; } + + if(Dmg > m_Armor) { Dmg -= m_Armor; m_Armor = 0; } else { m_Armor -= + Dmg; Dmg = 0; } } + + m_Health -= Dmg; } + + m_DamageTakenTick = Server()->Tick(); + + // do damage Hit sound if(From >= 0 && From != m_pPlayer->GetCID() && + GameServer()->m_apPlayers[From]) + GameServer()->CreateSound(GameServer()->m_apPlayers[From]->m_ViewPos, + SOUND_HIT, CmaskOne(From)); + + // check for death if(m_Health <= 0) { Die(From, Weapon); + + + + return false; } + + if (Dmg > 2) GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_LONG); + else GameServer()->CreateSound(m_Pos, SOUND_PLAYER_PAIN_SHORT); */ + // set attacker's face to happy (taunt!) + /* if(g_Config.m_SvEmotionalTees) { if (From >= 0 && From != + m_pPlayer->GetCID() && GameServer()->m_apPlayers[From]) { CCharacter + *pChr = GameServer()->m_apPlayers[From]->GetCharacter(); if (pChr) { + pChr->m_EmoteType = EMOTE_HAPPY; pChr->m_EmoteStop = Server()->Tick() + + Server()->TickSpeed(); } } */// Removed you can set your emote via /emoteEMOTENAME + // set the attacked face to pain + m_EmoteType = EMOTE_PAIN; + m_EmoteStop = Server()->Tick() + 500 * Server()->TickSpeed() / 1000; + + return true; +} + +void CCharacter::Snap(int SnappingClient) +{ + if (NetworkClipped(SnappingClient)) + return; + if (!GetPlayer()->m_ShowOthers && !Team()->InTeam(SnappingClient)) + return; + CNetObj_Character *Character = + static_cast < + CNetObj_Character * + >(Server()-> + SnapNewItem(NETOBJTYPE_CHARACTER, m_pPlayer->GetCID(), + sizeof(CNetObj_Character))); + + // write down the m_Core + if (!m_ReckoningTick || GameServer()->m_World.m_Paused) + { + // no dead reckoning when paused because the client doesn't know + // how far to perform the reckoning + Character->m_Tick = 0; + m_Core.Write(Character); + } + else + { + Character->m_Tick = m_ReckoningTick; + m_SendCore.Write(Character); + } + + if (m_DoSplash) + { + Character->m_Jumped = 3; + } + // set emote + if (m_EmoteStop < Server()->Tick()) + { + m_EmoteType = EMOTE_NORMAL; + m_EmoteStop = -1; + } + + Character->m_Emote = m_EmoteType; + + Character->m_AmmoCount = 0; + Character->m_Health = 0; + Character->m_Armor = 0; + + if (m_FreezeTime > 0 || m_FreezeTime == -1) + { + Character->m_Emote = EMOTE_PAIN; + Character->m_Weapon = WEAPON_NINJA; + Character->m_AmmoCount = 0; + } + else + Character->m_Weapon = m_ActiveWeapon; + Character->m_AttackTick = m_AttackTick; + + Character->m_Direction = m_Input.m_Direction; + + if (m_pPlayer->GetCID() == SnappingClient) + { + Character->m_Health = m_Health; + Character->m_Armor = m_Armor; + if (m_aWeapons[m_ActiveWeapon].m_Ammo > 0) + Character->m_AmmoCount = + (!m_FreezeTime) ? m_aWeapons[m_ActiveWeapon].m_Ammo : 0; + } + + if (Character->m_Emote == EMOTE_NORMAL) + { + if (250 - ((Server()->Tick() - m_LastAction) % (250)) < 5) + Character->m_Emote = EMOTE_BLINK; + } + + Character->m_PlayerState = m_PlayerState; +} diff --git a/src/game/server/gamemodes/ctf.cpp b/src/game/server/gamemodes/ctf.cpp index 623a02cdc..e192ce250 100644 --- a/src/game/server/gamemodes/ctf.cpp +++ b/src/game/server/gamemodes/ctf.cpp @@ -1,5 +1,5 @@ // copyright (c) 2007 magnus auvinen, see licence.txt for more info/* -#include +/*#include #include #include #include diff --git a/src/game/server/player.cpp~ b/src/game/server/player.cpp~ new file mode 100644 index 000000000..6ded3402b --- /dev/null +++ b/src/game/server/player.cpp~ @@ -0,0 +1,422 @@ +#include +#include +#include +#include +#include + +#include "player.h" +#include "gamecontext.h" +#include +#include "gamemodes/DDRace.h" + +MACRO_ALLOC_POOL_ID_IMPL (CPlayer, MAX_CLIENTS) + IServer *CPlayer::Server () const const +{ + return m_pGameServer->Server (); +} + +CPlayer::CPlayer (CGameContext * pGameServer, int CID, int Team) +{ + m_pGameServer = pGameServer; + m_RespawnTick = Server ()->Tick (); + m_DieTick = Server ()->Tick (); + m_ScoreStartTick = Server ()->Tick (); + Character = 0; + m_Muted = 0; + this->m_ClientID = CID; + m_Team = GameServer ()->m_pController->ClampTeam (Team); + + m_LastPlaytime = time_get (); + m_LastTarget_x = 0; + m_LastTarget_y = 0; + m_SentAfkWarning = 0; // afk timer's 1st warning after 50% of sv_max_afk_time + m_SentAfkWarning2 = 0; + + m_PauseInfo.m_Respawn = false; + + if (!g_Config.m_SvShowOthers) + m_ShowOthers = false; + else + m_ShowOthers = true; + + m_IsUsingRaceClient = false; + m_LastSentTime = 0; + + GameServer ()->Score ()->PlayerData (CID)->Reset (); +} + +CPlayer::~CPlayer () +{ + delete Character; + Character = 0; +} + +void +CPlayer::Tick () +{ + Server ()->SetClientAuthed (m_ClientID, m_Authed); + Server ()->SetClientScore (m_ClientID, m_Score); + + if (m_Muted > 0) + m_Muted--; + // do latency stuff + { + IServer::CClientInfo Info; + if (Server ()->GetClientInfo (m_ClientID, &Info)) + { + m_Latency.m_Accum += Info.m_Latency; + m_Latency.m_AccumMax = max (m_Latency.m_AccumMax, Info.m_Latency); + m_Latency.m_AccumMin = min (m_Latency.m_AccumMin, Info.m_Latency); + } + // each second + if (Server ()->Tick () % Server ()->TickSpeed () == 0) + { + m_Latency.m_Avg = m_Latency.m_Accum / Server ()->TickSpeed (); + m_Latency.m_Max = m_Latency.m_AccumMax; + m_Latency.m_Min = m_Latency.m_AccumMin; + m_Latency.m_Accum = 0; + m_Latency.m_AccumMin = 1000; + m_Latency.m_AccumMax = 0; + } + } + + if (!Character + && m_DieTick + Server ()->TickSpeed () * 3 <= Server ()->Tick ()) + m_Spawning = true; + + if (Character) + { + if (Character->IsAlive ()) + { + m_ViewPos = Character->m_Pos; + } + else + { + delete Character; + Character = 0; + } + } + else if (m_Spawning && m_RespawnTick <= Server ()->Tick ()) + TryRespawn (); + + // send best time + if (m_IsUsingRaceClient) + { + if (m_LastSentTime > GameServer ()->m_pController->m_CurrentRecord + || (m_LastSentTime == 0 + && GameServer ()->m_pController->m_CurrentRecord > 0)) + { + //dbg_msg("player", "Record message sended"); + char aBuf[16]; + str_format (aBuf, sizeof (aBuf), "%.0f", GameServer ()->m_pController->m_CurrentRecord * 100.0f); // damn ugly but the only way i know to do it + int TimeToSend; + sscanf (aBuf, "%d", &TimeToSend); + CNetMsg_Sv_Record Msg; + Msg.m_Time = TimeToSend; + Server ()->SendPackMsg (&Msg, MSGFLAG_VITAL, m_ClientID); + + m_LastSentTime = GameServer ()->m_pController->m_CurrentRecord; + } + } +} + +void +CPlayer::Snap (int SnappingClient) +{ + CNetObj_ClientInfo *ClientInfo = + static_cast < + CNetObj_ClientInfo * + >(Server ()-> + SnapNewItem (NETOBJTYPE_CLIENTINFO, m_ClientID, + sizeof (CNetObj_ClientInfo))); + StrToInts (&ClientInfo->m_Name0, 6, Server ()->ClientName (m_ClientID)); + StrToInts (&ClientInfo->m_Skin0, 6, m_TeeInfos.m_SkinName); + ClientInfo->m_UseCustomColor = m_TeeInfos.m_UseCustomColor; + ClientInfo->m_ColorBody = m_TeeInfos.m_ColorBody; + ClientInfo->m_ColorFeet = m_TeeInfos.m_ColorFeet; + + CNetObj_PlayerInfo *Info = + static_cast < + CNetObj_PlayerInfo * + >(Server ()-> + SnapNewItem (NETOBJTYPE_PLAYERINFO, m_ClientID, + sizeof (CNetObj_PlayerInfo))); + + Info->m_Latency = m_Latency.m_Min; + Info->m_LatencyFlux = m_Latency.m_Max - m_Latency.m_Min; + Info->m_Local = 0; + Info->m_ClientId = m_ClientID; + + + if (m_ClientID == SnappingClient) + Info->m_Local = 1; + + // send 0 if times of otheres are not shown + if (SnappingClient != m_ClientID) + Info->m_Score = 0; + else + Info->m_Score = m_Score; + + Info->m_Team = m_Team; +} + +void +CPlayer::OnDisconnect () +{ + KillCharacter (); + + if (Server ()->ClientIngame (m_ClientID)) + { + char aBuf[512]; + str_format (aBuf, sizeof (aBuf), "%s has left the game", + Server ()->ClientName (m_ClientID)); + GameServer ()->SendChat (-1, CGameContext::CHAT_ALL, aBuf); + char Cmd[64]; + str_format (aBuf, sizeof (aBuf), "leave player='%d:%s'", m_ClientID, + Server ()->ClientName (m_ClientID)); + GameServer ()->Console ()->Print (IConsole::OUTPUT_LEVEL_STANDARD, + "game", aBuf); + if (m_Muted > 0) + { + str_format (Cmd, sizeof (Cmd), "ban %d %d '%s'", m_ClientID, + m_Muted / Server ()->TickSpeed (), "ppc"); + GameServer ()->Console ()->ExecuteLine (Cmd, 3, -1); + } + } +} + +void +CPlayer::OnPredictedInput (CNetObj_PlayerInput * NewInput) +{ + if (Character) + Character->OnPredictedInput (NewInput); +} + +void +CPlayer::OnDirectInput (CNetObj_PlayerInput * NewInput) +{ + if (Character) + Character->OnDirectInput (NewInput); + + if (!Character && m_Team >= 0 && (NewInput->m_Fire & 1)) + m_Spawning = true; + + if (!Character && m_Team == -1) + m_ViewPos = vec2 (NewInput->m_TargetX, NewInput->m_TargetY); + AfkTimer (NewInput->m_TargetX, NewInput->m_TargetY); +} + +CCharacter * +CPlayer::GetCharacter () +{ + if (Character && Character->IsAlive ()) + return Character; + return 0; +} + +void +CPlayer::KillCharacter (int Weapon) +{ + if (Character) + { + Character->Die (m_ClientID, Weapon); + delete Character; + Character = 0; + } +} + +void +CPlayer::Respawn () +{ + if (m_Team > -1) + m_Spawning = true; +} + +void +CPlayer::SetTeam (int Team) +{ + // clamp the team + Team = GameServer ()->m_pController->ClampTeam (Team); + if (m_Team == Team) + return; + + char aBuf[512]; + str_format (aBuf, sizeof (aBuf), "%s joined the %s", + Server ()->ClientName (m_ClientID), + GameServer ()->m_pController->GetTeamName (Team)); + GameServer ()->SendChat (-1, CGameContext::CHAT_ALL, aBuf); + + KillCharacter (); + + m_Team = Team; + // we got to wait 0.5 secs before respawning + m_RespawnTick = Server ()->Tick () + Server ()->TickSpeed () / 2; + str_format (aBuf, sizeof (aBuf), "team_join player='%d:%s' m_Team=%d", + m_ClientID, Server ()->ClientName (m_ClientID), m_Team); + GameServer ()->Console ()->Print (IConsole::OUTPUT_LEVEL_DEBUG, "game", + aBuf); + + //GameServer()->m_pController->OnPlayerInfoChange(GameServer()->m_apPlayers[m_ClientID]); +} + +void +CPlayer::TryRespawn () +{ + if (m_PauseInfo.m_Respawn) + { + Character = new (m_ClientID) CCharacter (&GameServer ()->m_World); + Character->Spawn (this, m_PauseInfo.m_Core.m_Pos); + GameServer ()->CreatePlayerSpawn (m_PauseInfo.m_Core.m_Pos); + LoadCharacter (); + } + else + { + vec2 SpawnPos = vec2 (100.0f, -60.0f); + if (!GameServer ()->m_pController->CanSpawn (this, &SpawnPos)) + return; + + // check if the position is occupado + CEntity *apEnts[2] = { 0 }; + int NumEnts = + GameServer ()->m_World.FindEntities (SpawnPos, 64, apEnts, 2, + NETOBJTYPE_CHARACTER); + if (NumEnts < 3) + { + m_Spawning = false; + Character = new (m_ClientID) CCharacter (&GameServer ()->m_World); + Character->Spawn (this, SpawnPos); + GameServer ()->CreatePlayerSpawn (SpawnPos); + } + } +} + +void +CPlayer::LoadCharacter () +{ + Character->m_Core = m_PauseInfo.m_Core; + if (g_Config.m_SvPauseTime) + Character->m_StartTime = + Server ()->Tick () - (m_PauseInfo.m_PauseTime - + m_PauseInfo.m_StartTime); + Character->m_RaceState = m_PauseInfo.m_RaceState; + Character->m_RefreshTime = Server ()->Tick (); + for (int i = 0; i < NUM_WEAPONS; ++i) + { + if (m_PauseInfo.m_aHasWeapon[i]) + { + if (!m_PauseInfo.m_FreezeTime) + { + Character->GiveWeapon (i, -1); + } + else + { + Character->GiveWeapon (i, 0); + } + } + } + Character->m_FreezeTime = m_PauseInfo.m_FreezeTime; + Character->m_Doored = m_PauseInfo.m_Doored; + Character->m_OldPos = m_PauseInfo.m_OldPos; + Character->m_OlderPos = m_PauseInfo.m_OlderPos; + Character->m_LastAction = m_PauseInfo.m_LastAction; + Character->m_Jumped = m_PauseInfo.m_Jumped; + Character->m_Health = m_PauseInfo.m_Health; + Character->m_Armor = m_PauseInfo.m_Armor; + Character->m_PlayerState = m_PauseInfo.m_PlayerState; + Character->m_LastMove = m_PauseInfo.m_LastMove; + Character->m_PrevPos = m_PauseInfo.m_PrevPos; + Character->m_ActiveWeapon = m_PauseInfo.m_ActiveWeapon; + Character->m_LastWeapon = m_PauseInfo.m_LastWeapon; + Character->m_HammerType = m_PauseInfo.m_HammerType; + Character->m_Super = m_PauseInfo.m_Super; + m_PauseInfo.m_Respawn = false; +} + +void +CPlayer::SaveCharacter () +{ + m_PauseInfo.m_Core = Character->m_Core; + m_PauseInfo.m_StartTime = Character->m_StartTime; + m_PauseInfo.m_RaceState = Character->m_RaceState; + for (int i = 0; i < WEAPON_NINJA; ++i) + { + m_PauseInfo.m_aHasWeapon[i] = Character->m_aWeapons[i].m_Got; + } + m_PauseInfo.m_FreezeTime = Character->m_FreezeTime; + m_PauseInfo.m_Doored = Character->m_Doored; + m_PauseInfo.m_OldPos = Character->m_OldPos; + m_PauseInfo.m_OlderPos = Character->m_OlderPos; + m_PauseInfo.m_LastAction = Character->m_LastAction; + m_PauseInfo.m_Jumped = Character->m_Jumped; + m_PauseInfo.m_Health = Character->m_Health; + m_PauseInfo.m_Armor = Character->m_Armor; + m_PauseInfo.m_PlayerState = Character->m_PlayerState; + m_PauseInfo.m_LastMove = Character->m_LastMove; + m_PauseInfo.m_PrevPos = Character->m_PrevPos; + m_PauseInfo.m_ActiveWeapon = Character->m_ActiveWeapon; + m_PauseInfo.m_LastWeapon = Character->m_LastWeapon; + m_PauseInfo.m_HammerType = Character->m_HammerType; + m_PauseInfo.m_Super = Character->m_Super; + m_PauseInfo.m_PauseTime = Server ()->Tick (); + //m_PauseInfo.m_RefreshTime = Character->m_RefreshTime; +} + +void +CPlayer::AfkTimer (int new_target_x, int new_target_y) +{ + /* + afk timer (x, y = mouse coordinates) + Since a player has to move the mouse to play, this is a better method than checking + the player's position in the game world, because it can easily be bypassed by just locking a key. + Frozen players could be kicked as well, because they can't move. + It also works for spectators. + */ + + if (m_Authed) + return; // don't kick admins + if (g_Config.m_SvMaxAfkTime == 0) + return; // 0 = disabled + + if (new_target_x != m_LastTarget_x || new_target_y != m_LastTarget_y) + { + m_LastPlaytime = time_get (); + m_LastTarget_x = new_target_x; + m_LastTarget_y = new_target_y; + m_SentAfkWarning = 0; // afk timer's 1st warning after 50% of sv_max_afk_time + m_SentAfkWarning2 = 0; + + } + else + { + // not playing, check how long + if (m_SentAfkWarning == 0 + && m_LastPlaytime < + time_get () - time_freq () * (int) (g_Config.m_SvMaxAfkTime * 0.5)) + { + sprintf (m_pAfkMsg, + "You have been afk for %d seconds now. Please note that you get kicked after not playing for %d seconds.", + (int) (g_Config.m_SvMaxAfkTime * 0.5), + g_Config.m_SvMaxAfkTime); + m_pGameServer->SendChatTarget (m_ClientID, m_pAfkMsg); + m_SentAfkWarning = 1; + } + else if (m_SentAfkWarning2 == 0 + && m_LastPlaytime < + time_get () - + time_freq () * (int) (g_Config.m_SvMaxAfkTime * 0.9)) + { + sprintf (m_pAfkMsg, + "You have been afk for %d seconds now. Please note that you get kicked after not playing for %d seconds.", + (int) (g_Config.m_SvMaxAfkTime * 0.9), + g_Config.m_SvMaxAfkTime); + m_pGameServer->SendChatTarget (m_ClientID, m_pAfkMsg); + m_SentAfkWarning = 1; + } + else if (m_LastPlaytime < + time_get () - time_freq () * g_Config.m_SvMaxAfkTime) + { + CServer *serv = (CServer *) m_pGameServer->Server (); + serv->Kick (m_ClientID, "Away from keyboard"); + } + } +}