From e083136aa837599ae8af5868ddf1e12c928a56d4 Mon Sep 17 00:00:00 2001 From: wootguy Date: Wed, 30 Oct 2024 21:06:51 -0700 Subject: [PATCH] misc plugin api updates Copied more helpers from mmlib. Exposed more funcs. Added more hooks. --- dlls/CMakeLists.txt | 24 +++ dlls/PluginHooks.h | 12 ++ dlls/PluginManager.cpp | 2 +- dlls/animation.h | 42 ++-- dlls/cbase.h | 5 +- dlls/client.cpp | 2 + dlls/debug.h | 4 +- dlls/eng_wrappers.cpp | 16 ++ dlls/eng_wrappers.h | 2 + dlls/enginecallback.h | 9 +- dlls/monster/CShockTrooper.cpp | 14 +- dlls/mstream.cpp | 4 +- dlls/mstream.h | 2 + dlls/net/IPV4.cpp | 97 +++++++++ dlls/net/IPV4.h | 31 +++ dlls/net/Packet.cpp | 65 ++++++ dlls/net/Packet.h | 40 ++++ dlls/net/Socket.h | 79 ++++++++ dlls/net/Socket_unix.cpp | 355 +++++++++++++++++++++++++++++++++ dlls/net/Socket_win.cpp | 346 ++++++++++++++++++++++++++++++++ dlls/net/network_unix.cpp | 71 +++++++ dlls/net/network_win.cpp | 98 +++++++++ dlls/util.cpp | 50 ++++- dlls/util.h | 28 ++- dlls/weapon/CShockBeam.cpp | 8 +- 25 files changed, 1354 insertions(+), 52 deletions(-) create mode 100644 dlls/net/IPV4.cpp create mode 100644 dlls/net/IPV4.h create mode 100644 dlls/net/Packet.cpp create mode 100644 dlls/net/Packet.h create mode 100644 dlls/net/Socket.h create mode 100644 dlls/net/Socket_unix.cpp create mode 100644 dlls/net/Socket_win.cpp create mode 100644 dlls/net/network_unix.cpp create mode 100644 dlls/net/network_win.cpp diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 300c0cde..cb3e611d 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -455,6 +455,22 @@ set(UTIL_SRC ../game_shared/shared_util.cpp ) +set(NET_HDR + net/Socket.h + net/IPV4.h + net/Packet.h +) +set(NET_SRC + net/IPV4.cpp + net/Packet.cpp +) + +if (UNIX) + set(NET_SRC ${NET_SRC} net/network_unix.cpp net/Socket_unix.cpp) +else() + set(NET_SRC ${NET_SRC} net/network_win.cpp net/Socket_win.cpp) +endif() + set(GAME_HDR client.h ../engine/custom.h @@ -570,6 +586,7 @@ set(ALL_SRC ${GENERATED_SRCS} ${ENT_SRC} ${ENT_HDR} ${UTIL_SRC} ${UTIL_HDR} + ${NET_SRC} ${NET_HDR} ${GAME_SRC} ${GAME_HDR} ${MISC_HDR} ) @@ -596,6 +613,8 @@ if(UNIX) LINK_FLAGS "-m32 -g ${ASAN_LFLAGS}" ) + target_link_libraries(${PROJECT_NAME} PRIVATE WS2_32 IPHLPAPI) + elseif(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEF:${CMAKE_CURRENT_SOURCE_DIR}/hl.def") @@ -617,6 +636,7 @@ elseif(MSVC) source_group("Header Files\\Entities\\weapon\\ammo" FILES ${AMMO_HDR}) source_group("Header Files\\Game" FILES ${GAME_HDR}) source_group("Header Files\\Util" FILES ${UTIL_HDR}) + source_group("Header Files\\Network" FILES ${NET_HDR}) source_group("Source Files\\Entities" FILES ${ENTITY_SRC}) source_group("Source Files\\Generated" FILES ${GENERATED_SRCS}) @@ -636,7 +656,11 @@ elseif(MSVC) source_group("Source Files\\Entities\\weapon" FILES ${WEAPON_SRC}) source_group("Source Files\\Entities\\weapon\\ammo" FILES ${AMMO_SRC}) source_group("Source Files\\Util" FILES ${UTIL_SRC}) + source_group("Source Files\\Network" FILES ${NET_SRC}) source_group("Source Files\\Game" FILES ${GAME_SRC}) + + # WinSock libraries for network utils + target_link_libraries(${PROJECT_NAME} PRIVATE WS2_32 IPHLPAPI) else() message(FATAL_ERROR "TODO: Mac support") diff --git a/dlls/PluginHooks.h b/dlls/PluginHooks.h index 725be10e..d3b25180 100644 --- a/dlls/PluginHooks.h +++ b/dlls/PluginHooks.h @@ -107,6 +107,18 @@ struct HLCOOP_PLUGIN_HOOKS { HOOK_RETURN_DATA (*pfnWriteString)(const char* value); HOOK_RETURN_DATA (*pfnWriteEntity)(int value); HOOK_RETURN_DATA (*pfnMessageEnd)(); + + // called before the engine sets the model, after model replacement in the mod + HOOK_RETURN_DATA (*pfnSetModel)(edict_t* edict, const char* model); + + // called immediately after the engine sets a model + HOOK_RETURN_DATA (*pfnSetModelPost)(edict_t* edict, const char* model); + + // called after the engine precaches the given model + HOOK_RETURN_DATA (*pfnPrecacheModelPost)(const char* model); + + // called before an event is played + HOOK_RETURN_DATA(*pfnPlaybackEvent)(int flags, const edict_t* pInvoker, unsigned short eventindex, float delay, float* origin, float* angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2); }; EXPORT void RegisterPlugin(void* plugin, HLCOOP_PLUGIN_HOOKS* hooks, const char* name); diff --git a/dlls/PluginManager.cpp b/dlls/PluginManager.cpp index 08000b53..2ee36104 100644 --- a/dlls/PluginManager.cpp +++ b/dlls/PluginManager.cpp @@ -203,7 +203,7 @@ void PluginManager::UpdateServerPlugins(bool forceUpdate) { { lineNum++; - int endPos = line.find_first_of("#/"); + int endPos = line.find("//"); if (endPos != -1) line = trimSpaces(line.substr(0, endPos)); diff --git a/dlls/animation.h b/dlls/animation.h index 24c5af98..6cd22e7b 100644 --- a/dlls/animation.h +++ b/dlls/animation.h @@ -21,34 +21,34 @@ #include "monsterevent.h" #endif -extern int IsSoundEvent( int eventNumber ); +EXPORT int IsSoundEvent( int eventNumber ); // Ranomly pick a sequence tagged with the given activity -int LookupActivity( void *pmodel, entvars_t *pev, int activity ); +EXPORT int LookupActivity( void *pmodel, entvars_t *pev, int activity ); // Pick the sequence with the heaviest random chance -int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ); +EXPORT int LookupActivityHeaviest( void *pmodel, entvars_t *pev, int activity ); // Find the n'th sequence tagged with the given activity. If the offset is greater than // the number of sequences tagged with this activity, then the last found sequence is returned. -int LookupActivityWithOffset(void* pmodel, entvars_t* pev, int activity, int offset); - -bool ActivityHasEvent(void* pmodel, int activity, int event); - -int LookupSequence( void *pmodel, const char *label ); -void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed ); -int GetSequenceFlags( void *pmodel, entvars_t *pev ); -int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd ); -float SetController( void *pmodel, entvars_t *pev, int iController, float flValue ); -float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ); -void GetEyePosition( void *pmodel, float *vecEyePosition ); -void SequencePrecache( void *pmodel, const char *pSequenceName ); -int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ); -void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ); -int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ); - -int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ); -int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ); +EXPORT int LookupActivityWithOffset(void* pmodel, entvars_t* pev, int activity, int offset); + +EXPORT bool ActivityHasEvent(void* pmodel, int activity, int event); + +EXPORT int LookupSequence( void *pmodel, const char *label ); +EXPORT void GetSequenceInfo( void *pmodel, entvars_t *pev, float *pflFrameRate, float *pflGroundSpeed ); +EXPORT int GetSequenceFlags( void *pmodel, entvars_t *pev ); +EXPORT int LookupAnimationEvents( void *pmodel, entvars_t *pev, float flStart, float flEnd ); +EXPORT float SetController( void *pmodel, entvars_t *pev, int iController, float flValue ); +EXPORT float SetBlending( void *pmodel, entvars_t *pev, int iBlender, float flValue ); +EXPORT void GetEyePosition( void *pmodel, float *vecEyePosition ); +EXPORT void SequencePrecache( void *pmodel, const char *pSequenceName ); +EXPORT int FindTransition( void *pmodel, int iEndingAnim, int iGoalAnim, int *piDir ); +EXPORT void SetBodygroup( void *pmodel, entvars_t *pev, int iGroup, int iValue ); +EXPORT int GetBodygroup( void *pmodel, entvars_t *pev, int iGroup ); + +EXPORT int GetAnimationEvent( void *pmodel, entvars_t *pev, MonsterEvent_t *pMonsterEvent, float flStart, float flEnd, int index ); +EXPORT int ExtractBbox( void *pmodel, int sequence, float *mins, float *maxs ); // From /engine/studio.h #define STUDIO_LOOPING 0x0001 diff --git a/dlls/cbase.h b/dlls/cbase.h index e3b09bcf..819eef2f 100644 --- a/dlls/cbase.h +++ b/dlls/cbase.h @@ -115,6 +115,7 @@ class CBaseMonster; class CBasePlayerWeapon; class CTalkSquadMonster; class CBaseToggle; +class CBaseAnimating; #define SF_NORESPAWN ( 1 << 10 )// !!!set this bit on guns and stuff that should never respawn. @@ -204,6 +205,7 @@ class EXPORT CBaseEntity virtual CBasePlayerWeapon* GetWeaponPtr(void) { return NULL; }; virtual CTalkSquadMonster * MyTalkSquadMonsterPointer( void ) { return NULL;} virtual CBaseToggle* MyTogglePointer(void) { return NULL; } + virtual CBaseAnimating* MyAnimatingPointer(void) { return NULL; } virtual int GetToggleState( void ) { return TS_AT_TOP; } virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {} virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {} @@ -289,7 +291,7 @@ class EXPORT CBaseEntity int IsDormant( void ); BOOL IsLockedByMaster( void ) { return FALSE; } - static CBaseEntity *Instance( edict_t *pent ) + static CBaseEntity *Instance( const edict_t *pent ) { if ( !pent ) pent = ENT(0); @@ -519,6 +521,7 @@ class EXPORT CBaseAnimating : public CBaseDelay virtual int GetEntindexPriority() { return ENTIDX_PRIORITY_NORMAL; } virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); + virtual CBaseAnimating* MyAnimatingPointer(void) { return this; } static TYPEDESCRIPTION m_SaveData[]; diff --git a/dlls/client.cpp b/dlls/client.cpp index a7ee99a8..5d996018 100644 --- a/dlls/client.cpp +++ b/dlls/client.cpp @@ -817,6 +817,8 @@ void StartFrame( void ) lagcomp_update(); g_Scheduler.Think(); + + handleThreadPrints(); } diff --git a/dlls/debug.h b/dlls/debug.h index 04ae9cd2..75b74085 100644 --- a/dlls/debug.h +++ b/dlls/debug.h @@ -66,9 +66,9 @@ std::string getLogTimeStr(); void writeDebugLog(std::ofstream& outFile, std::string lastLogName, std::string prefix, std::string line); -const char* msgDestStr(int msg_dest); +EXPORT const char* msgDestStr(int msg_dest); -const char* msgTypeStr(int msg_type); +EXPORT const char* msgTypeStr(int msg_type); void log_msg(msg_info& msg); diff --git a/dlls/eng_wrappers.cpp b/dlls/eng_wrappers.cpp index 231b9312..81b03d16 100644 --- a/dlls/eng_wrappers.cpp +++ b/dlls/eng_wrappers.cpp @@ -428,6 +428,8 @@ int PRECACHE_MODEL(const char* path) { world->v.modelindex = oldModelIdx; } + CALL_HOOKS(int, pfnPrecacheModelPost, path); + return modelIdx; } else { @@ -491,8 +493,12 @@ bool SET_MODEL(edict_t* edict, const char* model) { model = NOT_PRECACHED_MODEL; } + CALL_HOOKS(bool, pfnSetModel, edict, model); + g_engfuncs.pfnSetModel(edict, model); + CALL_HOOKS(bool, pfnSetModelPost, edict, model); + return replaced; } @@ -652,4 +658,14 @@ const char* CMD_ARGS() { void CHANGE_LEVEL(const char* pszLevelName, const char* pszLandmarkName) { CALL_HOOKS_VOID(pfnChangeLevel, pszLevelName, pszLandmarkName); g_engfuncs.pfnChangeLevel(pszLevelName, pszLandmarkName); +} + +void PLAYBACK_EVENT_FULL(int flags, const edict_t* pInvoker, unsigned short eventindex, float delay, + float* origin, float* angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, + int bparam2) +{ + CALL_HOOKS_VOID(pfnPlaybackEvent, flags, pInvoker, eventindex, delay, origin, angles, fparam1, fparam2, + iparam1, iparam2, bparam1, bparam2); + g_engfuncs.pfnPlaybackEvent(flags, pInvoker, eventindex, delay, origin, angles, fparam1, fparam2, + iparam1, iparam2, bparam1, bparam2); } \ No newline at end of file diff --git a/dlls/eng_wrappers.h b/dlls/eng_wrappers.h index d493118d..612bc9b2 100644 --- a/dlls/eng_wrappers.h +++ b/dlls/eng_wrappers.h @@ -46,6 +46,7 @@ inline void MESSAGE_BEGIN(int msg_dest, int msg_type, const float* pOrigin = NUL #define CMD_ARGC (*g_engfuncs.pfnCmd_Argc) #define CMD_ARGV (*g_engfuncs.pfnCmd_Argv) #define CHANGE_LEVEL (*g_engfuncs.pfnChangeLevel) +#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) #else // engine wrappers which handle model/sound replacement logic EXPORT int PRECACHE_GENERIC(const char* path); @@ -86,4 +87,5 @@ EXPORT const char* CMD_ARGV(int argc); EXPORT int CMD_ARGC(); EXPORT const char* CMD_ARGS(); EXPORT void CHANGE_LEVEL(const char* pszLevelName, const char* pszLandmarkName); +EXPORT void PLAYBACK_EVENT_FULL(int flags, const edict_t* pInvoker, unsigned short eventindex, float delay, float* origin, float* angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2); #endif \ No newline at end of file diff --git a/dlls/enginecallback.h b/dlls/enginecallback.h index eacd2a6e..bf28e389 100644 --- a/dlls/enginecallback.h +++ b/dlls/enginecallback.h @@ -95,17 +95,18 @@ EXPORT void DEBUG_MSG(ALERT_TYPE target, const char* format, ...); #if defined(PLUGIN_BUILD) && defined(PLUGIN_NAME) #define ALERT(target, fmt, ...) { \ - DEBUG_MSG(target, "[" PLUGIN_NAME "] " fmt, ##__VA_ARGS__ ); \ + DEBUG_MSG(target, "[" PLUGIN_NAME "] "); \ + DEBUG_MSG(target, fmt, ##__VA_ARGS__ ); \ } #else #define ALERT DEBUG_MSG #endif #define print(...) {ALERT(at_console, __VA_ARGS__);} -#define println(...) {ALERT(at_console, __VA_ARGS__); ALERT(at_console, "\n");} +#define println(...) {ALERT(at_console, __VA_ARGS__); DEBUG_MSG(at_console, "\n");} #define ENGINE_FPRINTF (*g_engfuncs.pfnEngineFprintf) #define ALLOC_PRIVATE (*g_engfuncs.pfnPvAllocEntPrivateData) -inline void *GET_PRIVATE( edict_t *pent ) +inline void *GET_PRIVATE( const edict_t *pent ) { if ( pent ) return pent->pvPrivateData; @@ -136,8 +137,6 @@ inline void *GET_PRIVATE( edict_t *pent ) #define NUMBER_OF_ENTITIES (*g_engfuncs.pfnNumberOfEntities) #define IS_DEDICATED_SERVER (*g_engfuncs.pfnIsDedicatedServer) -#define PLAYBACK_EVENT_FULL (*g_engfuncs.pfnPlaybackEvent) - #define ENGINE_SET_PVS (*g_engfuncs.pfnSetFatPVS) #define ENGINE_SET_PAS (*g_engfuncs.pfnSetFatPAS) diff --git a/dlls/monster/CShockTrooper.cpp b/dlls/monster/CShockTrooper.cpp index 123baca9..d6ced2ae 100644 --- a/dlls/monster/CShockTrooper.cpp +++ b/dlls/monster/CShockTrooper.cpp @@ -965,13 +965,13 @@ void CShockTrooper::HandleAnimEvent(MonsterEvent_t* pEvent) GetAttachment(0, vecArmPos, vecArmAngles); MESSAGE_BEGIN(MSG_PVS, SVC_TEMPENTITY, vecArmPos); - g_engfuncs.pfnWriteByte(TE_SPRITE); - g_engfuncs.pfnWriteCoord(vecArmPos.x); - g_engfuncs.pfnWriteCoord(vecArmPos.y); - g_engfuncs.pfnWriteCoord(vecArmPos.z); - g_engfuncs.pfnWriteShort(iShockTrooperMuzzleFlash); - g_engfuncs.pfnWriteByte(4); - g_engfuncs.pfnWriteByte(128); + WRITE_BYTE(TE_SPRITE); + WRITE_COORD(vecArmPos.x); + WRITE_COORD(vecArmPos.y); + WRITE_COORD(vecArmPos.z); + WRITE_COORD(iShockTrooperMuzzleFlash); + WRITE_BYTE(4); + WRITE_BYTE(128); MESSAGE_END(); Shoot(); diff --git a/dlls/mstream.cpp b/dlls/mstream.cpp index dd8df9be..fee8192b 100644 --- a/dlls/mstream.cpp +++ b/dlls/mstream.cpp @@ -265,7 +265,9 @@ bool mstream::eom() void mstream::freeBuf() { - delete [] (char*)start; + if (start) + delete [] (char*)start; + start = 0; } mstream::~mstream( void ) diff --git a/dlls/mstream.h b/dlls/mstream.h index a4f4da05..3d1744d1 100644 --- a/dlls/mstream.h +++ b/dlls/mstream.h @@ -1,4 +1,6 @@ #pragma once +#include "Platform.h" +#include "vector.h" #include class EXPORT mstream diff --git a/dlls/net/IPV4.cpp b/dlls/net/IPV4.cpp new file mode 100644 index 00000000..8908f503 --- /dev/null +++ b/dlls/net/IPV4.cpp @@ -0,0 +1,97 @@ +#include "extdll.h" +#include "util.h" +#include "IPV4.h" +#include + +using namespace std; + +IPV4::IPV4() : b1(0), b2(0), b3(0), b4(0), port(0) {} + +IPV4::IPV4(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint16_t port) + : b1(b1), b2(b2), b3(b3), b4(b4), port(port) {} + + +IPV4::IPV4(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4 ) + : b1(b1), b2(b2), b3(b3), b4(b4), port(0) {} + + +int parseOctet(string& s) +{ + int dot = s.find_first_of("."); + if (dot == -1) + return -1; + int octet = atoi(s.substr(0,dot).c_str()); + s = s.substr(dot+1); + return octet; +} + +IPV4::IPV4( const char * addr ) +{ + b1 = b2 = b3 = b4 = 0; + port = 0; + string s = addr; + + if (s.size() == 0) + { + b1 = b2 = b3 = b4 = port = 0; + return; + } + + int o1 = parseOctet(s); + int o2 = parseOctet(s); + int o3 = parseOctet(s); + + if (o1 == -1 || o2 == -1 || o3 == -1) + { + ALERT(at_console, "error parsing IPV4 string: %s", addr); + b1 = b2 = b3 = b4 = port = 0; + return; + } + + b1 = o1; + b2 = o2; + b3 = o3; + + int colon = s.find_first_of(":"); + if (colon != -1) + { + b4 = atoi(s.substr(0, colon).c_str()); + s = s.substr(colon+1); + port = atoi(s.c_str()); + } + else + b4 = atoi(s.c_str()); +} + +string IPV4::getString() const +{ + return getHostString() + ":" + to_string(port); +} + +string IPV4::getHostString() const +{ + return to_string(b1) + "." + to_string(b2) + "." + to_string(b3) + "." + to_string(b4); +} + +bool IPV4::isEmpty() const +{ + return !b1 && !b2 && !b3 && !b4 && !port; +} + +bool operator==( IPV4 a, IPV4 b ) +{ + return a.b1 == b.b1 && + a.b2 == b.b2 && + a.b3 == b.b3 && + a.b4 == b.b4 && + a.port == b.port; +} + +bool operator!=( IPV4 a, IPV4 b ) +{ + return a.b1 != b.b1 || + a.b2 != b.b2 || + a.b3 != b.b3 || + a.b4 != b.b4 || + a.port != b.port; +} diff --git a/dlls/net/IPV4.h b/dlls/net/IPV4.h new file mode 100644 index 00000000..3ff1ae93 --- /dev/null +++ b/dlls/net/IPV4.h @@ -0,0 +1,31 @@ +#pragma once +#include "extdll.h" +#include +#include + +class EXPORT IPV4 +{ +public: + uint8_t b1, b2, b3, b4; + uint16_t port; + + IPV4(); + + IPV4(const char * addr); + + IPV4(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4); + + IPV4(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint16_t port); + + // 4 octets and port + std::string getString() const; + + // 4 octets - no port + std::string getHostString() const; + + // is everything 0? + bool isEmpty() const; +}; + +EXPORT bool operator==( IPV4 a, IPV4 b ); +EXPORT bool operator!=( IPV4 a, IPV4 b ); \ No newline at end of file diff --git a/dlls/net/Packet.cpp b/dlls/net/Packet.cpp new file mode 100644 index 00000000..ff2f2cb3 --- /dev/null +++ b/dlls/net/Packet.cpp @@ -0,0 +1,65 @@ +#include "Packet.h" +#include + +using namespace std; + +Packet::Packet(const Packet& other) { + this->addr = other.addr; + this->sz = other.sz; + this->data = new char[sz]; + memcpy(this->data, other.data, sz); +} + +Packet::Packet(const string& message) +{ + init(message); +} + +Packet::Packet(const string& message, IPV4 addr) : addr(addr) +{ + init(message); +} + +Packet::Packet(IPV4 addr, const void * data, int sz) : addr(addr), sz(sz) +{ + this->data = new char[sz]; + memcpy(this->data, data, sz); +} + +Packet::Packet(const void * data, int sz) : sz(sz) +{ + this->data = new char[sz]; + memcpy(this->data, data, sz); +} + +Packet::Packet() +{ + data = NULL; + sz = -1; +} + +Packet::~Packet() +{ + if (data != NULL) + delete [] data; +} + +string Packet::getString() { + char* outDat = new char[sz + 1]; + memcpy(outDat, data, sz); + outDat[sz] = 0; + + string outStr = outDat; + delete[] outDat; + + return outStr; +} + +void Packet::init(const string& message) +{ + sz = message.size(); + data = new char[sz+1]; + memcpy(data, (char*)&message[0], sz); + data[sz] = 0; + sz++; +} \ No newline at end of file diff --git a/dlls/net/Packet.h b/dlls/net/Packet.h new file mode 100644 index 00000000..e9ff5d1c --- /dev/null +++ b/dlls/net/Packet.h @@ -0,0 +1,40 @@ +#pragma once +#include "extdll.h" +#include "IPV4.h" +#include + +// maximum size before fragmentation. +// MTU for IPV4 UDP is 576 byte. +// IP headers can be between 20 and 60 bytes. +// UDP headers are always 8 bytes. +// 576 - 68 = 508 +//#define MAX_PACKET_SIZE 508 + +#define MAX_PACKET_SIZE 4096 // fragmentation is ok for this program + +class EXPORT Packet +{ +public: + IPV4 addr; // destination/source address + char * data = NULL; + int sz = -1; + + Packet(const Packet& other); + + Packet(const std::string& message); + + Packet(const std::string& message, IPV4 addr); + + Packet(IPV4 addr, const void * data, int sz); + + Packet(const void * data, int sz); + + std::string getString(); + + Packet(); + + ~Packet(); + +private: + void init(const std::string& message); +}; \ No newline at end of file diff --git a/dlls/net/Socket.h b/dlls/net/Socket.h new file mode 100644 index 00000000..9b58300e --- /dev/null +++ b/dlls/net/Socket.h @@ -0,0 +1,79 @@ +#pragma once +#include "extdll.h" +#include "Packet.h" + +class IPV4; + +bool initNet(); +void netStop(); +IPV4 getLocalIP(); + +// IP headers are AT LEAST 20/40 bytes, maybe more +// UDP headers are always 8 bytes +#define IPV4_UDP_HEADER_SIZE (20+8) +#define IPV6_UDP_HEADER_SIZE (48+8) +#define IPV4_TCP_HEADER_SIZE (36+8) +#define IPV6_TCP_HEADER_SIZE (64+8) + +// Socket flags +#define SOCKET_UDP 0 +#define SOCKET_TCP 1 +#define SOCKET_NONBLOCKING 0 +#define SOCKET_BLOCKING 2 +#define SOCKET_SERVER 4 // Set/cleared automatically when socket is created + +// platform-specific socket data +struct SocketData; + +class EXPORT Socket +{ +public: + // keeps track of throughput on this socket + uint32_t sendBytes; + uint32_t recvBytes; + + // creates an invalid socket + Socket(); + + // creates and binds a server socket + Socket(int socketType, uint16_t port); + + // creates a client socket + Socket(int socketType, IPV4 addr); + + // closes the connection + ~Socket(void); + + // returns false if no packets were received + bool recv(Packet& p); + + // Servers should specify an address for the packet + // and clients should leave it blank + bool send(const Packet& p); + + // Only clients should use this information since + // no address is specified + bool send(const void * dat, int sz); + + inline bool isServer() { return socketType & SOCKET_SERVER; } + inline bool isTCP() { return socketType & SOCKET_TCP; } + inline bool isBlocking() { return socketType & SOCKET_BLOCKING; } + + // create the TCP connection to the server + // timeout is given in milliseconds. + bool connect(uint32_t timeout); + + // accepts a TCP connection on this socket + // Returns NULL if there are no pending connections + Socket * accept(); + +private: + void * skt; // platform-specific data + int socketType; + + // Lets the server start receiving packets + bool bind(); + + // creates a platform-specific socket + SocketData * createSocket(const char * addr, const char * port); +}; \ No newline at end of file diff --git a/dlls/net/Socket_unix.cpp b/dlls/net/Socket_unix.cpp new file mode 100644 index 00000000..559831bc --- /dev/null +++ b/dlls/net/Socket_unix.cpp @@ -0,0 +1,355 @@ +#include "extdll.h" +#include "util.h" +#include "Socket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct SocketData +{ + int sock; + sockaddr_in addr; + sockaddr_in dest; +}; + +#ifndef SOMAXCONN + #define SOMAXCONN 64 +#endif + +SocketData * Socket::createSocket(const char * addr, const char * port) +{ + SocketData * sock = new SocketData; + memset(sock, 0, sizeof(SocketData)); + + if (socketType & SOCKET_TCP) sock->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else sock->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (sock->sock == -1) + { + ALERT(at_console, "Socket creation failed\n"); + delete sock; + return NULL; + } + + if (!(socketType & SOCKET_BLOCKING)) + { + int nFlags = fcntl(sock->sock, F_GETFL, 0); + nFlags |= O_NONBLOCK; + if (fcntl(sock->sock, F_SETFL, nFlags) == -1) + ALERT(at_console, "Error setting socket to non-blocking\n"); + } + + return sock; +} + +Socket::Socket() +{ + socketType = 0; + sendBytes = recvBytes = 0; + SocketData * sock = new SocketData; + memset(sock, 0, sizeof(SocketData)); + skt = sock; +} + +Socket::Socket(int socketType, ushort port) +{ + this->socketType = socketType | SOCKET_SERVER; + sendBytes = recvBytes = 0; + skt = createSocket(0,0); + + if (skt != NULL) + { + SocketData * sock = (SocketData*)skt; + sock->addr.sin_family = AF_INET; + sock->addr.sin_port = htons(port); + sock->addr.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind()) { + ALERT(at_console, "Created server socket on port %d\n", (int)port); + } + else { + ALERT(at_console, "Failed to bind socket\n"); + } + } + +} + +Socket::Socket(int socketType, IPV4 addr) +{ + this->socketType = socketType & ~SOCKET_SERVER; + sendBytes = recvBytes = 0; + skt = createSocket(0,0); + + if (skt != NULL) + { + SocketData * sock = (SocketData*)skt; + sock->dest.sin_family = AF_INET; + sock->dest.sin_port = htons(addr.port); + string sAddr = addr.getHostString(); + int ret = inet_aton(sAddr.c_str(), &sock->dest.sin_addr); + if (ret == 0) + { + ALERT(at_console, "inet_aton() failed\n"); + delete sock; + return; + } + ALERT(at_console, "Created client socket on %s\n", addr.getString().c_str()); + } +} + +Socket::~Socket(void) +{ + SocketData * sock = (SocketData*)skt; + if (sock != NULL) + { + close(sock->sock); + delete sock; + } +} + +bool Socket::connect(uint timeout) +{ + if (!(socketType & SOCKET_TCP)) + { + ALERT(at_console, "Can't connect() with a UDP socket.\n"); + return false; + } + if (skt == NULL) + { + ALERT(at_console, "Can't connect. Invalid socket!\n"); + return false; + } + + SocketData * sock = (SocketData*)skt; + int ret = ::connect(sock->sock, (const sockaddr*)&sock->dest, sizeof(sock->dest)); + if (ret) + { + int err = errno; + char buff[256]; + if (err == EALREADY || err == EINPROGRESS) // Would block + { + fd_set set; + FD_ZERO(&set); + FD_SET(sock->sock, &set); + timeval tv; + tv.tv_sec = (timeout / 1000); + tv.tv_usec = (timeout % 1000)*1000; + ret = select(sock->sock + 1, NULL, &set, NULL, &tv); + + if (ret == 0) + { + ALERT(at_console, "Socket connection timed out\n"); + return false; + } + else if (ret == -1) + { + ALERT(at_console, "Failed to connect socket: %s\n", strerror_r( err, buff, 256 )); + return false; + } + } + else + { + ALERT(at_console, "Failed to connect socket: %s\n", strerror_r( err, buff, 256 )); + return false; + } + } + + return true; +} + +Socket * Socket::accept() +{ + if (!(socketType & SOCKET_TCP)) + { + ALERT(at_console, "Can't accept() with a UDP socket.\n"); + return NULL; + } + if (skt == NULL) + { + ALERT(at_console, "Can't accept. Invalid socket!\n"); + return NULL; + } + SocketData * sock = (SocketData*)skt; + + socklen_t len = sizeof(sockaddr_in); + sockaddr_in newAddr; + int newSock = ::accept(sock->sock, (sockaddr*)&newAddr, &len); + if (newSock == -1) + { + int err = errno; + if (err == EAGAIN) // Would block + return NULL; + + char buff[256]; + ALERT(at_console, "Socket accept failed: %s\n", strerror_r( err, buff, 256 )); + return NULL; + } + + Socket * newClient = new Socket(); + newClient->socketType = this->socketType; + SocketData* newData = (SocketData*)newClient->skt; + newData->addr = sock->addr; + newData->dest = newAddr; + newData->sock = newSock; + + return newClient; +} + +bool Socket::bind() +{ + SocketData * sock = (SocketData*)skt; + int ret = ::bind(sock->sock, (sockaddr*) &sock->addr, sizeof(sock->addr)); + if (ret < 0) + { + ALERT(at_console, "Failed to bind socket\n"); + return false; + } + if (socketType & SOCKET_TCP) + { + ret = listen(sock->sock, SOMAXCONN); + int err = errno; + char buff[256]; + if (ret < 0) + { + ALERT(at_console, "Socket listen failed with error: %s\n", strerror_r( err, buff, 256 )); + close(sock->sock); + delete skt; + skt = NULL; + return false; + } + + fd_set set; + FD_ZERO(&set); + FD_SET(sock->sock, &set); + + int timeout = 1000; + timeval tv; + tv.tv_sec = (timeout / 1000); + tv.tv_usec = (timeout % 1000)*1000; + ret = select(sock->sock + 1, &set, NULL, NULL, &tv); + + err = errno; + if (ret == 0) + { + ALERT(at_console, "Socket listen connection timed out\n"); + return false; + } + else if (ret == -1) + { + ALERT(at_console, "Failed to listen socket: %s\n", strerror_r( err, buff, 256 )); + return false; + } + } + return true; +} + +bool Socket::recv(Packet& p) +{ + p.data = NULL; + p.sz = -1; + + if (skt == NULL) + { + ALERT(at_console, "Can't recv. Invalid socket!\n"); + return false; + } + SocketData * sock = (SocketData*)skt; + sockaddr_in addr; + socklen_t slen = sizeof(addr); + char dat[MAX_PACKET_SIZE]; + int len; + + if (socketType & SOCKET_TCP) + len = ::recv(sock->sock, dat, MAX_PACKET_SIZE, 0); + else + len = recvfrom(sock->sock, dat, MAX_PACKET_SIZE, 0, (sockaddr*)&addr, &slen); + + if (len == 0) + return false; + if (len == -1) + { + int err = errno; + if ((err == EAGAIN) || (err == EWOULDBLOCK)) + return false; + char buff[256]; + ALERT(at_console, "Socket recv failed with error: %s\n", strerror_r( err, buff, 256 )); + return false; + } + + uint a = addr.sin_addr.s_addr; + byte b4 = (a >> 24) & 0xff; + byte b3 = (a >> 16) & 0xff; + byte b2 = (a >> 8) & 0xff; + byte b1 = a & 0xff; + IPV4 sender(b1, b2, b3, b4, ntohs(addr.sin_port)); + + if (p.data != NULL) + delete [] p.data; + p.addr = sender; + p.data = new char[len]; + memcpy(p.data, dat, len); + p.sz = len; + recvBytes += len + (socketType & SOCKET_TCP) ? IPV4_TCP_HEADER_SIZE : IPV4_UDP_HEADER_SIZE; + return true; +} + +bool Socket::send( const Packet& p ) +{ + if (skt == NULL) + { + ALERT(at_console, "Can't send. Invalid socket!\n"); + return false; + } + if (p.sz > MAX_PACKET_SIZE) + { + ALERT(at_console, "Packet too big to send! (%d > %d)\n", p.sz, MAX_PACKET_SIZE); + return false; + } + + SocketData * sock = (SocketData*)skt; + sockaddr_in addr; + if (!p.addr.isEmpty()) + { + string saddr = p.addr.getHostString(); + memset(&addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr( saddr.c_str() ); + addr.sin_port = htons(p.addr.port); + } + else + addr = sock->dest; + + int ret; + if (socketType & SOCKET_TCP) ret = ::send(sock->sock, p.data, p.sz, MSG_NOSIGNAL); + else ret = sendto(sock->sock, p.data, p.sz, 0, (sockaddr*)&addr, sizeof(addr)); + + char buff[256]; + if ( ret < 0 ) + { + int err = errno; + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + ALERT(at_console, "Send would block so don't send\n"); + return false; + } + ALERT(at_console, "Socket send failed with error: %s\n", strerror_r( err, buff, 256 )); + return false; + } + + sendBytes += ret + (socketType & SOCKET_TCP) ? IPV4_TCP_HEADER_SIZE : IPV4_UDP_HEADER_SIZE; + return true; +} + +bool Socket::send( const void * dat, int sz ) +{ + return send(Packet(dat, sz)); +} diff --git a/dlls/net/Socket_win.cpp b/dlls/net/Socket_win.cpp new file mode 100644 index 00000000..7930b189 --- /dev/null +++ b/dlls/net/Socket_win.cpp @@ -0,0 +1,346 @@ +#include "extdll.h" +#include "util.h" +#include "Socket.h" +#include + +using namespace std; + +addrinfo tcphints = +{ + AI_PASSIVE, // AI_PASSIVE + PF_INET, + SOCK_STREAM, // datagram socket type + IPPROTO_TCP, // TCP + 0, 0, 0, 0 +}; + +addrinfo udphints = +{ + AI_PASSIVE, // AI_PASSIVE + PF_INET, + SOCK_DGRAM, // datagram socket type + IPPROTO_UDP, // UDP + 0, 0, 0, 0 +}; + +struct SocketData +{ + SOCKET sock; + addrinfo * addr; // local socket address + sockaddr_in dest; // destination address +}; + +SocketData * Socket::createSocket(const char * addr, const char * port) +{ + SocketData * skt = new SocketData; + // Resolve the local address and port to be used by the server + addrinfo * hints = (socketType & SOCKET_TCP) ? &tcphints : &udphints; + int ret = getaddrinfo(addr, port, hints, &skt->addr); + + if (ret != 0) + { + ALERT(at_console, "Socket creation failed at getaddrinfo(): %d\n", WSAGetLastError()); + delete skt; + return NULL; + } + + // create the socket + skt->sock = socket(skt->addr->ai_family, skt->addr->ai_socktype, skt->addr->ai_protocol); + + if (skt->sock == INVALID_SOCKET) + { + ALERT(at_console, "Socket creation failed at socket(): %d\n", WSAGetLastError()); + freeaddrinfo(skt->addr); + delete skt; + return NULL; + } + + // set socket to non-blocking + if (!(socketType & SOCKET_BLOCKING)) + { + u_long iMode = 1; + ret = ioctlsocket(skt->sock, FIONBIO, &iMode); + if (ret == SOCKET_ERROR) + { + ALERT(at_console, "Failed to set socket to non-blocking: %d\n", WSAGetLastError()); + freeaddrinfo(skt->addr); + delete skt; + return NULL; + } + } + + if (socketType & SOCKET_UDP) { + BOOL bNewBehavior = FALSE; + DWORD dwBytesReturned = 0; + WSAIoctl(skt->sock, WSAECONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL); + } + + return skt; +} + +Socket::Socket() +{ + socketType = 0; + sendBytes = recvBytes = 0; + SocketData * sock = new SocketData; + memset(sock, 0, sizeof(SocketData)); + skt = sock; +} + +Socket::Socket(int socketType, uint16_t port) +{ + this->socketType = socketType | SOCKET_SERVER; + sendBytes = recvBytes = 0; + skt = createSocket(NULL, to_string(port).c_str()); + if (skt != NULL) + { + if (bind()) + ALERT(at_console, "Server socket created on port %d\n", port); + } +} + +Socket::Socket(int socketType, IPV4 addr) +{ + this->socketType = socketType & ~SOCKET_SERVER; + sendBytes = recvBytes = 0; + SocketData * sock = createSocket(addr.getHostString().c_str(), NULL); + skt = sock; + if (sock != NULL) + { + memset(&sock->dest, 0, sizeof(sockaddr_in)); + sock->dest.sin_family = AF_INET; + sock->dest.sin_addr.s_addr = inet_addr( addr.getHostString().c_str() ); + sock->dest.sin_port = htons(addr.port); + ALERT(at_console, "Client socket created on %s:%d\n", inet_ntoa(sock->dest.sin_addr), (int)ntohs(sock->dest.sin_port) ); + } +} + +Socket::~Socket(void) +{ + SocketData * sock = (SocketData*)skt; + if (skt != NULL) + { + closesocket(sock->sock); + if (sock->addr != NULL) + freeaddrinfo(sock->addr); + delete sock; + } +} + +bool Socket::connect(uint32_t timeout) +{ + if (!(socketType & SOCKET_TCP)) + { + ALERT(at_console, "Can't connect() with a UDP socket.\n"); + return false; + } + if (skt == NULL) + { + ALERT(at_console, "Can't connect(). Invalid socket!\n"); + return false; + } + + SocketData * sock = (SocketData*)skt; + int ret = ::connect(sock->sock, (const sockaddr*)&sock->dest, sizeof(sock->dest)); + if (ret) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + { + fd_set set; + set.fd_array[0] = sock->sock; + set.fd_count = 1; + timeval tv; + tv.tv_sec = (timeout / 1000); + tv.tv_usec = (timeout % 1000)*1000; + ret = ::select(NULL, &set, &set, NULL, &tv); + + if (ret == 0) + { + ALERT(at_console, "Socket connection timed out\n"); + return false; + } + else if (ret == SOCKET_ERROR) + { + ALERT(at_console, "Failed to connect socket: %d\n", err); + return false; + } + } + else if (err == WSAEISCONN) { + return true; // already connected + } + else + { + ALERT(at_console, "Failed to connect socket: %d\n", err); + return false; + } + } + return true; +} + +Socket * Socket::accept() +{ + if (!(socketType & SOCKET_TCP)) + { + ALERT(at_console, "Can't accept() with a UDP socket.\n"); + return NULL; + } + if (skt == NULL) + { + ALERT(at_console, "Can't accept(). Invalid socket!\n"); + return NULL; + } + SocketData * sock = (SocketData*)skt; + sockaddr_in newAddr; + int len = sizeof(sockaddr_in); + SOCKET newSock = ::accept(sock->sock, (sockaddr*)&newAddr, &len); + if (newSock == INVALID_SOCKET) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + return NULL; + + ALERT(at_console, "Socket accept failed: %d\n", err); + return NULL; + } + + Socket * newClient = new Socket(); + newClient->socketType = this->socketType; + SocketData* newData = (SocketData*)newClient->skt; + newData->addr = sock->addr; + newData->dest = newAddr; + newData->sock = newSock; + + return newClient; +} + +// only called for server sockets +bool Socket::bind() +{ + if (skt == NULL) + { + ALERT(at_console, "Can't bind. Invalid socket!\n"); + return false; + } + SocketData * sock = (SocketData*)skt; + + int iResult = ::bind( sock->sock, sock->addr->ai_addr, (int)sock->addr->ai_addrlen); + + if (iResult == SOCKET_ERROR) + { + ALERT(at_console, "Socket bind failed with error: %d\n", WSAGetLastError()); + closesocket(sock->sock); + sock->sock = INVALID_SOCKET; + return false; + } + + if (socketType & SOCKET_TCP) + { + iResult = listen(sock->sock, SOMAXCONN); + if (iResult == SOCKET_ERROR) + { + ALERT(at_console, "Socket listen failed with error: %d\n", WSAGetLastError()); + closesocket(sock->sock); + sock->sock = INVALID_SOCKET; + return false; + } + } + return true; +} + +bool Socket::recv(Packet& p) +{ + p.data = NULL; + p.sz = -1; + + if (skt == NULL) + { + ALERT(at_console, "Can't receive. Invalid socket!\n"); + return false; + } + SocketData * sock = (SocketData*)skt; + sockaddr_in addr = sock->dest; + int addrLen = sizeof(addr); + char dat[MAX_PACKET_SIZE]; + int packetLen; + + if (socketType & SOCKET_TCP) + packetLen = ::recv(sock->sock, dat, MAX_PACKET_SIZE, 0); + else + packetLen = ::recvfrom(sock->sock, dat, MAX_PACKET_SIZE, 0, (sockaddr*)&addr, &addrLen); + + if (packetLen == 0) + return false; + if (packetLen == -1) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + return false; + ALERT(at_console, "Socket recv failed with error: %d\n", err); + return false; + } + + byte b1 = addr.sin_addr.S_un.S_un_b.s_b1; + byte b2 = addr.sin_addr.S_un.S_un_b.s_b2; + byte b3 = addr.sin_addr.S_un.S_un_b.s_b3; + byte b4 = addr.sin_addr.S_un.S_un_b.s_b4; + IPV4 sender(b1, b2, b3, b4, ntohs(addr.sin_port)); + + if (p.data != NULL) + delete [] p.data; + p.addr = sender; + p.data = new char[packetLen]; + memcpy(p.data, dat, packetLen); + p.sz = packetLen; + recvBytes += packetLen + (socketType & SOCKET_TCP) ? IPV4_TCP_HEADER_SIZE : IPV4_UDP_HEADER_SIZE; + return true; +} + +bool Socket::send( const Packet& p ) +{ + if (skt == NULL) + { + ALERT(at_console, "Can't send. Invalid socket!\n"); + return false; + } + if (p.sz > MAX_PACKET_SIZE) + { + ALERT(at_console, "Packet too big to send! (%d > %d)\n", p.sz, MAX_PACKET_SIZE); + return false; + } + + SocketData * sock = (SocketData*)skt; + sockaddr_in addr; + if (!p.addr.isEmpty()) + { + string saddr = p.addr.getHostString(); + memset(&addr.sin_zero, 0, sizeof(addr.sin_zero)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr( saddr.c_str() ); + addr.sin_port = htons(p.addr.port); + } + else + addr = sock->dest; + + int ret; + if (socketType & SOCKET_TCP) ret = ::send(sock->sock, p.data, p.sz, 0); + else ret = sendto(sock->sock, p.data, p.sz, 0, (sockaddr*)&addr, sizeof(addr)); + + if (ret == 0) + return false; + if ( ret < 0 ) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + return false; + ALERT(at_console, "Socket send failed with error: %d\n", err); + return false; + } + sendBytes += ret + (socketType & SOCKET_TCP) ? IPV4_TCP_HEADER_SIZE : IPV4_UDP_HEADER_SIZE; + return true; +} + +bool Socket::send(const void * dat, int sz ) +{ + return send(Packet(dat, sz)); +} diff --git a/dlls/net/network_unix.cpp b/dlls/net/network_unix.cpp new file mode 100644 index 00000000..11b08930 --- /dev/null +++ b/dlls/net/network_unix.cpp @@ -0,0 +1,71 @@ +#include "extdll.h" +#include "util.h" +#include "Socket.h" +#include "IPV4.h" + +#include +#include +#include +#include +#include +#include +#include + +bool initNet() +{ + return true; +} + +void netStop() +{ + +} + +IPV4 getLocalIP() +{ + struct ifaddrs *ifaddr; + + if (getifaddrs(&ifaddr) == -1) + { + ALERT(at_console, "getifaddrs() failed\n"); + return IPV4(); + } + + bool found = false; + IPV4 out; + + struct ifaddrs *ifa = ifaddr; + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr != NULL) + { + int family = ifa->ifa_addr->sa_family; + if (family == AF_INET)// || family == AF_INET6) + { + char ip_addr[NI_MAXHOST]; + int s = getnameinfo(ifa->ifa_addr, ((family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), ip_addr, sizeof(ip_addr), NULL, 0, NI_NUMERICHOST); + if (s != 0) + { + ALERT(at_console, "getnameinfo() failed: %s\n", gai_strerror(s)); + freeifaddrs(ifaddr); + return IPV4(); + } + else + { + //print(ifa->ifa_name + " " + ip_addr); + IPV4 addr(ip_addr); + if (addr == IPV4(127,0,0,1)) + continue; + if (found) + { + ALERT(at_console, "Multiple local ip addresses found!\n"); + continue; + } + out = addr; + } + } + } + } + + return out; +} \ No newline at end of file diff --git a/dlls/net/network_win.cpp b/dlls/net/network_win.cpp new file mode 100644 index 00000000..19d7f79a --- /dev/null +++ b/dlls/net/network_win.cpp @@ -0,0 +1,98 @@ +#include "extdll.h" +#include "util.h" +#include "IPV4.h" +#include +#include +#include + +using namespace std; + +bool needInit = true; + +bool initNet() +{ + if (!needInit) + return true; + + WSAData wsaData; + WSAStartup(0, &wsaData); // get version + + // load specific winsock version + if (WSAStartup(wsaData.wHighVersion, &wsaData) == 0) + { + uint16_t v = wsaData.wHighVersion; + ALERT(at_console, "Loaded winsock v%d.%d\n", (int)(v >> 8), (int)(v & 0xff)); + return true; + } + ALERT(at_console, "Winsock failed to load\n"); + needInit = false; + return false; +} + +void netStop() +{ + WSACleanup(); +} + +void print_adapter(PIP_ADAPTER_ADDRESSES aa) +{ + char buf[BUFSIZ]; + memset(buf, 0, BUFSIZ); + WideCharToMultiByte(CP_ACP, 0, aa->FriendlyName, wcslen(aa->FriendlyName), buf, BUFSIZ, NULL, NULL); + ALERT(at_console, "adapter_name: %s", buf); +} + +// https://gist.github.com/yoggy/1241986 +IPV4 getLocalIP() +{ + int flags = 0; + DWORD size; + PIP_ADAPTER_ADDRESSES adapter_addresses, aa; + PIP_ADAPTER_UNICAST_ADDRESS ua; + + int ret = GetAdaptersAddresses(AF_INET, flags, NULL, NULL, &size); + if (ret != ERROR_BUFFER_OVERFLOW) + { + ALERT(at_error, "Initial GetAdaptersAddresses() failed...\n"); + return IPV4(); + } + adapter_addresses = (PIP_ADAPTER_ADDRESSES)malloc(size); + + ret = GetAdaptersAddresses(AF_INET, flags, NULL, adapter_addresses, &size); + if (ret != 0) + { + ALERT(at_error, "GetAdaptersAddresses() failed...\n"); + return IPV4(); + } + + bool found = false; + IPV4 pick; + for (aa = adapter_addresses; aa != NULL; aa = aa->Next) { + //print_adapter(aa); + for (ua = aa->FirstUnicastAddress; ua != NULL; ua = ua->Next) + { + char buf[BUFSIZ]; + memset(buf, 0, BUFSIZ); + getnameinfo(ua->Address.lpSockaddr, ua->Address.iSockaddrLength, buf, sizeof(buf), NULL, 0,NI_NUMERICHOST); + IPV4 addr(buf); + if (addr == IPV4(127,0,0,1)) // ignore the loop-back address + continue; + else if (!found) + { + found = true; + pick = addr; + } + else + { + if (pick.b1 == 169) // indicates that windows can't find a DCHP server to get an address from + pick = addr; + else if (addr != pick) + ALERT(at_console, "Multiple local addresses found! Ignoring %s", addr.getString().c_str()); + } + } + } + + free(adapter_addresses); + + return pick; +} \ No newline at end of file diff --git a/dlls/util.cpp b/dlls/util.cpp index f5cab390..5c596f8c 100644 --- a/dlls/util.cpp +++ b/dlls/util.cpp @@ -91,6 +91,9 @@ std::string g_mp3Command; std::unordered_map g_admins; +std::thread::id g_main_thread_id = std::this_thread::get_id(); +ThreadSafeQueue g_thread_prints; + TYPEDESCRIPTION gEntvarsDescription[] = { DEFINE_ENTITY_FIELD(classname, FIELD_STRING), @@ -1062,14 +1065,14 @@ const char* BreakupLongLines(const char* pMessage) { return pMessage; } -void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage ) +void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage, int msgMode) { if ( !pEntity || !pEntity->IsNetClient() ) return; pMessage = BreakupLongLines(pMessage); - MESSAGE_BEGIN( MSG_ONE, SVC_TEMPENTITY, NULL, pEntity->edict() ); + MESSAGE_BEGIN(msgMode, SVC_TEMPENTITY, NULL, pEntity->edict() ); WRITE_BYTE( TE_TEXTMESSAGE ); WRITE_BYTE( textparms.channel & 0xFF ); @@ -2564,6 +2567,11 @@ void DEBUG_MSG(ALERT_TYPE target, const char* format, ...) { vsnprintf(log_line, 4096, format, vl); va_end(vl); + if (std::this_thread::get_id() != g_main_thread_id) { + g_thread_prints.enqueue({target, log_line}); // only the main thread can call engine functions + return; + } + #if defined(WIN32) && (_DEBUG) OutputDebugString(log_line); #endif @@ -2585,6 +2593,19 @@ void DEBUG_MSG(ALERT_TYPE target, const char* format, ...) { } } +void handleThreadPrints() { + AlertMsgCall msg; + + for (int failsafe = 0; failsafe < 128; failsafe++) { + if (g_thread_prints.dequeue(msg)) { + ALERT(msg.atype, msg.msg.c_str()); + } + else { + break; + } + } +} + Vector VecBModelOrigin(entvars_t* pevBModel) { return pevBModel->absmin + (pevBModel->size * 0.5); @@ -2654,8 +2675,6 @@ double TimeDifference(uint64_t start, uint64_t end) { void LoadAdminList(bool forceUpdate) { const char* ADMIN_LIST_FILE = CVAR_GET_STRING("adminlistfile"); - g_admins.clear(); - static uint64_t lastEditTime = 0; std::string fpath = getGameFilePath(ADMIN_LIST_FILE); @@ -2680,6 +2699,8 @@ void LoadAdminList(bool forceUpdate) { lastEditTime = editTime; + g_admins.clear(); + std::string line; while (std::getline(infile, line)) { if (line.empty()) { @@ -2687,7 +2708,7 @@ void LoadAdminList(bool forceUpdate) { } // strip comments - int endPos = line.find_first_of("#/"); + int endPos = line.find("//"); if (endPos != -1) line = trimSpaces(line.substr(0, endPos)); @@ -2802,3 +2823,22 @@ std::vector getDirFiles(std::string path, std::string extension, st return results; } + +void KickPlayer(edict_t* ent, const char* reason) { + if (!ent || (ent->v.flags & FL_CLIENT) == 0) { + return; + } + int userid = g_engfuncs.pfnGetPlayerUserId(ent); + g_engfuncs.pfnServerCommand(UTIL_VarArgs("kick #%d %s\n", userid, reason)); + g_engfuncs.pfnServerExecute(); +} + +// https://stackoverflow.com/questions/1628386/normalise-orientation-between-0-and-360 +float normalizeRangef(const float value, const float start, const float end) +{ + const float width = end - start; + const float offsetValue = value - start; // value relative to 0 + + return (offsetValue - (floor(offsetValue / width) * width)) + start; + // + start to reset back to start of original range +} \ No newline at end of file diff --git a/dlls/util.h b/dlls/util.h index b59e6ffa..f5a2a22b 100644 --- a/dlls/util.h +++ b/dlls/util.h @@ -40,6 +40,8 @@ #include "debug.h" #include "eng_wrappers.h" #include "wav.h" +#include "ThreadSafeQueue.h" +#include class CBasePlayer; @@ -57,6 +59,14 @@ extern std::string g_mp3Command; // current global mp3 command extern TYPEDESCRIPTION gEntvarsDescription[]; extern const int ENTVARS_COUNT; +struct AlertMsgCall { + ALERT_TYPE atype; + std::string msg; +}; + +extern std::thread::id g_main_thread_id; +extern ThreadSafeQueue g_thread_prints; + #define NOT_PRECACHED_MODEL "models/" MOD_MODEL_FOLDER "not_precached.mdl" #define MERGED_ITEMS_MODEL "models/" MOD_MODEL_FOLDER "w_items_v2.mdl" #define NOT_PRECACHED_SOUND "common/null.wav" @@ -227,11 +237,11 @@ inline entvars_t *VARS(edict_t *pent) } inline entvars_t* VARS(EOFFSET eoffset) { return VARS(ENT(eoffset)); } -inline int ENTINDEX(edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } +inline int ENTINDEX(const edict_t *pEdict) { return (*g_engfuncs.pfnIndexOfEdict)(pEdict); } inline edict_t* INDEXENT( int iEdictNum ) { return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); } -inline uint32_t PLRBIT(edict_t* pEdict) { return 1 << (ENTINDEX(pEdict) & 31); } +inline uint32_t PLRBIT(const edict_t* pEdict) { return 1 << (ENTINDEX(pEdict) & 31); } inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ) { - (*g_engfuncs.pfnMessageBegin)(msg_dest, msg_type, pOrigin, ENT(ent)); + MESSAGE_BEGIN(msg_dest, msg_type, pOrigin, ENT(ent)); } void WRITE_BYTES(uint8_t* bytes, int count); @@ -435,7 +445,7 @@ typedef struct hudtextparms_s // prints as transparent 'title' to the HUD EXPORT void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ); -EXPORT void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage ); +EXPORT void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage, int msgMode = MSG_ONE); // for handy use with ClientPrint params EXPORT char *UTIL_dtos1( int d ); @@ -803,4 +813,12 @@ EXPORT std::vector getDirFiles(std::string path, std::string extens EXPORT short FixedSigned16(float value, float scale); -EXPORT unsigned short FixedUnsigned16(float value, float scale); \ No newline at end of file +EXPORT unsigned short FixedUnsigned16(float value, float scale); + +EXPORT void KickPlayer(edict_t* ent, const char* reason=""); + +// Normalizes any number to an arbitrary range +// by assuming the range wraps around when going below min or above max +EXPORT float normalizeRangef(const float value, const float start, const float end); + +EXPORT void handleThreadPrints(); \ No newline at end of file diff --git a/dlls/weapon/CShockBeam.cpp b/dlls/weapon/CShockBeam.cpp index c2139dc7..a0056e7f 100644 --- a/dlls/weapon/CShockBeam.cpp +++ b/dlls/weapon/CShockBeam.cpp @@ -243,10 +243,10 @@ void CShockBeam::BallTouch( CBaseEntity* pOther ) UTIL_DecalTrace( &tr, DECAL_OFSCORCH1 + RANDOM_LONG( 0, 2 ) ); MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); - g_engfuncs.pfnWriteByte( TE_SPARKS ); - g_engfuncs.pfnWriteCoord( pev->origin.x ); - g_engfuncs.pfnWriteCoord( pev->origin.y ); - g_engfuncs.pfnWriteCoord( pev->origin.z ); + WRITE_BYTE( TE_SPARKS ); + WRITE_COORD( pev->origin.x ); + WRITE_COORD( pev->origin.y ); + WRITE_COORD( pev->origin.z ); MESSAGE_END(); } }