mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
Removed arbitrary and hidden restrictions on refreshing list of online players shown with 'wholist command
Fixed an issue where player ghosts that get teleported would not see themselves get updated to new location
Fixed a server crash from clients disconnecting while running server in debug mode
Improved how container updates are sent to players; now only sends to clients that have actually opened the container, and who are still within range
Added more information about UO data files being loaded during server startup
Enhanced the GM 'add menu to allow clicking directly on images of items/npcs to add them, and adjusted size of menus to accommodate this
Fixed an issue with fleeing NPCs that could cause them to attempt to flee across time and space, regardless of distance to target/attacker in combat
Added NPC AI that runs away from players if they get too close (even outside combat). Applied to hind, rabbits, squirrels, ferrets and various birds by default:
AI_ANIMAL_SCARED (aitype 12)
Fixed issues with NPCs fleeing forever, or getting stuck in flee/don't flee loop, by introducing max fleeing distance (50 tiles) and cooldown (30 sec) on fleeing
Updated onNameRequest JS Event to include third parameter with origin of name request, so script responses can be tailored appropriately:
onNameRequest( myObj, requestedBy, requestSource )
Potential values for requestSource:
0 - Speech/System Messages
1 - Guild Menus
2 - Stat Window (self)
3 - Stat Window (other)
4 - Tooltip
5 - Paperdoll Journal
6 - Paperdoll
7 - Single Click / All-Names
8 - System
9 - Secure Trade Window
Added Adaptive Performance System (APS) that dynamically adjust how often NPC AI/movement is checked based on overall shard performance. If performance drops below defined threshold, UOX3 gradually slows down checks for NPC AI/movement to prioritize player movement/speech/command responsiveness. If performance climbs back up above threshold, slowdowns are gradually removed. The following UOX.INI options have been added under [system] category to support this system:
APSPERFTHRESHOLD=50 // Performance threshold (measured in simulation cycles/sec) below which the APS kicks in
APSINTERVAL=100 // How often (in milliseconds) the APS checks performance and makes adjustments (if needed) to balance out shard performance
APSDELAYSTEP=50 // How much the delay timer is modified by (in milliseconds) each time APS makes adjustments
APSDELAYMAXCAP=2000 // Max amount of of delay APS can introduce for NPC AI/movement handling when attempting to restore shard performance
Added new JS Methods for Region objects:
.GetOrePrefs( oreType ) // Get ore preference data for ore type found in town region. Returned as an array containing the following data:
orePrefData -> [
oreName, // name of ore
color, // color of ore
minskill, // min skill to mine ore
ingotName, // name of ingot created from ore
makemenu, // makemenu entry for crafting something from ingot
oreChance, // default global chance of finding this ore type
scriptID // script attached to mined ore
],
orePrefChance // Chance of finding this ore type in given town region
.GetOreChance() // Get base chance of finding any ore in town region
Moved Mining skill plus gravedigging feature from hard code to scripts (js/skill/mining.js) to make it easier to maintain and/or customize, and removed hard coded variants
Gravedigging now relies on the ore resource system behind the scenes to restrict how often graves in a given area can be dug up
Fixed some incorrect references to .worldNumber Character property in misc scripts (should be .worldnumber)
Made some "hard-scripted" system messages in misc scripts use dictionary system instead
Added spawn region for banker NPCs in Serpent's Hold (dfndata/spawn/felucca/spawn_town_serpents_hold.dfn, dfndata/spawn/trammel/spawn_town_serpents_hold.dfn)
Fixed region definition of Ocllo to actually cover the entire town (dfndata/regions/regions.dfn)
Added numerous additional locations accessible with 'goplace # command, for key locations in Ilshenar, Malas, Tokuno Islands and Ter Mur (dfndata/location/location.dfn)
Revised travel-menu portion of GM menu (shortcut: 'travel) to include more travel options based on new locations (dfndata/items/travelmenu.dfn and travelmenu.bulk.dfn)
Fixed an issue where NPCs could attempt to attack targets in other worlds/instances
Added new JS Event that triggers for characters who are about to deal damage in combat. Complimentary to onDamage, which triggers for chars receiving damage:
onDamageDeal( dmgDealer, dmgReceiver, damageValue, damageType )
Added new JS Event that can trigger in global script upon creation of new player chars. Note that this will trigger in place of onCreateDFN event for player characters:
onCreatePlayer( pChar )
Added new JS Event that triggers for characters selecting a target with a spell. Complimentary to onSpellTarget, which triggers for targets selected with a spell:
onSpellTargetSelect( caster, target, spellNum )
Updated onCombatStart and onCombatEnd JS Events to also trigger for the other party in combat
Updated onPickup JS Event to include a third parameter - the potential container item was picked up from. Event now also triggers event in scripts attached to said containers:
onPickup( iPickedUp, pChar, iCont )
Added tracking of total playtime per individual character, and per account across all characters, and exposed these properties to JS engine:
.totalPlayTime // Account property, total playtime across all chars
.playTime // Character property, total playtime on given character only
Added new player-accessible command ('playtime) to spit out the playtime of current character/account as a whole (js/commands/playtime.js)
Implemented first version of Young Player System:
Replaced the UNUSED9 account flag with YOUNG, to be used by Young/New Player System
Added new JS Account property that gets/sets whether player account is considered Young:
.isYoung
Added new Char timers:
TIMER_YOUNGHEAL // Restricts how often Young players are healed by NPC healers
TIMER_YOUNGMESSAGE // Restricts how often Young players are warned about dangerous looking monsters in overworld
Updated GM commands 'get and 'set to get/set .isYoung property of player's account (js/commands/targeting/get.js and set.js)
Added new UOX.INI setting to enable/disable Young Player System (enabled by default):
YOUNGPLAYERSYSTEM=1
If Young Player System is enabled, all newly created player accounts are automatically marked with Young flag
Added new script to handle various restrictions and functions related to Young characters (js/player/young_player.js):
Young characters will have [Young] displayed over their head
Young characters will have their Young status checked and verified on every login + every stat/skill gain, to revoke the Young status if any of the following is true:
Account has a total playtime of more than 40 hours
Any character has more than 350 total (base) skill points
Any character has more than 70 skill points in a single skill
Any character has more than 150 total stat points
Any character has more than 80 stat points in a single stat
Young characters can renounce their Young status manually by saying the words "I renounce my young player status"
Young characters get two additional items upon creation:
a new player ticket (can be combined with any other players new player ticket for both players to get a reward)
a sextant (shows Young players directions to nearest moongate/bank regardless of where they are, as long as outdoors)
Young characters cannot target other player characters with hostile spells or skills
Young characters cannot be the target of other player characters' hostile spells or skills
Young characters can only cast beneficial spells upon themselves or other Young characters
Young characters can only be the target of beneficial spells from other Young players
Young players (and their pets/followers) cannot attack or harm any other players (or their pets/followers), nor themselves be attacked or harmed by any other players (or their pets/followers), whether from combat, spells, skills or items
Young players don't lose any of their items on death, and are teleported to the nearest healer upon death with all their items intact
Young players receive a warning upon entering dungeons that monsters there will be hostile
Updated global script (js/server/global.js) to:
Check/Verify Young status of players upon login
Attach young player script on login/creation for players on Young accounts
Give specific items to Young players upon creation
Added new script (js/item/corpse.js) that is assigned to all freshly created corpses. This script handles restrictions related to Young players interactions with corpses, and the interactions of other players with corpses of Young players
Updated JS Method .Carve() to return true/false depending on whether carving a corpse was successful
Updated Healer AI in code to heal nearby injured Young players
Updated Evil AI and Evil Caster AI in code to avoid selecting Young players as targets in combat outside of dungeons
Added a new section to UOX.INI - [young player starting locations] - that can be used to define starting locations for Young players. If only one such location is provided, all players start there. Otherwise, it matches the same starting location setup as the regular one - with one entry per town in Britannia.
Restricted transferring and/or friending of pets (code) and hirelings (hirelings.js) between Young and non-Young players
Restricted Young players from recalling or gating to Felucca facet
Young players can instantly logout from anywhere, at any time
1610 lines
50 KiB
C++
1610 lines
50 KiB
C++
#include "uox3.h"
|
||
#include "network.h"
|
||
#include "movement.h"
|
||
#include "wholist.h"
|
||
#include "skills.h"
|
||
#include "cMagic.h"
|
||
#include "CJSMapping.h"
|
||
#include "cScript.h"
|
||
#include "cEffects.h"
|
||
#include "cThreadQueue.h"
|
||
#include "CPacketSend.h"
|
||
#include "scriptc.h"
|
||
#include "ssection.h"
|
||
#include "cServerDefinitions.h"
|
||
#include "Dictionary.h"
|
||
#include "mapstuff.h"
|
||
#include <cctype>
|
||
|
||
#include "ObjectFactory.h"
|
||
#include "StringUtility.hpp"
|
||
#include "osunique.hpp"
|
||
|
||
#if PLATFORM != WINDOWS
|
||
#include <sys/ioctl.h>
|
||
#endif
|
||
|
||
#include <iomanip>
|
||
#include <memory>
|
||
|
||
using namespace std::string_literals;
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| bytebuffer exceptions
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//o------------------------------------------------------------------------------------------------o
|
||
|
||
ByteBufferBounds_st::ByteBufferBounds_st( int offset, int amount, int size ): std::out_of_range( "" ), offset( offset ), amount( amount ), buffersize( size )
|
||
{
|
||
_msg = "Offset : "s + std::to_string( offset ) + " Amount: "s + std::to_string( amount ) + " exceeds buffer size of: "s + std::to_string( size );
|
||
}
|
||
//=========================================================
|
||
auto ByteBufferBounds_st::what() const noexcept -> const char*
|
||
{
|
||
return _msg.c_str();
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| ByteBuffer_t
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| strutil functions
|
||
//o------------------------------------------------------------------------------------------------o
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - ByteBuffer_t::ntos
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Convert a bool to a string
|
||
//| the true_value/false_value are returned based on the bool
|
||
//o------------------------------------------------------------------------------------------------o
|
||
auto ByteBuffer_t::ntos( bool value, const std::string &true_value, const std::string &false_value ) -> std::string
|
||
{
|
||
return ( value ? true_value : false_value );
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - ByteBuffer_t::DumpByteBuffer()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Formatted dump of a byte buffer
|
||
//o------------------------------------------------------------------------------------------------o
|
||
auto ByteBuffer_t::DumpByteBuffer( std::ostream &output, const std::uint8_t *buffer, std::size_t length, radix_t radix, std::size_t entries_line ) -> void
|
||
{
|
||
// number of characters for entry
|
||
auto entry_size = 3; // decimal and octal
|
||
switch( static_cast<int>( radix ))
|
||
{
|
||
case static_cast<int>( radix_t::hex ):
|
||
entry_size = 2;
|
||
break;
|
||
case static_cast<int>( radix_t::bin ):
|
||
entry_size = 8;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
auto num_rows = ( length / entries_line ) + ((( length % entries_line ) == 0 ) ? 0 : 1 );
|
||
// what is the largest number for the address ?
|
||
auto max_address_chars = static_cast<int>(( ntos( num_rows * entries_line )).size() );
|
||
|
||
// first write out the header
|
||
output << std::setw( max_address_chars + 2 ) << "" << std::setw( 1 );
|
||
for( std::size_t i=0; i < entries_line; ++i )
|
||
{
|
||
output << ntos( i, radix_t::dec, false, entry_size, ' ' ) << " ";
|
||
}
|
||
output << "\n";
|
||
|
||
// now we write out the values for each line
|
||
std::string text( entries_line, ' ' );
|
||
|
||
for( std::size_t i = 0; i < length; ++i )
|
||
{
|
||
auto row = i / entries_line;
|
||
if((( i % static_cast<size_t>( entries_line ) == 0 ) && ( i >= static_cast<size_t>( entries_line ))) || ( i == 0 ))
|
||
{
|
||
// This is a new line!
|
||
output << ntos( row * entries_line, radix_t::dec, false, max_address_chars, ' ' ) << ": ";
|
||
text = std::string( entries_line, ' ' );
|
||
}
|
||
output << ntos( buffer[i], radix, false, entry_size ) << " ";
|
||
// If it is an alpha, we want to write it
|
||
if( std::isalpha( static_cast<int>( buffer[i] )) != 0 )
|
||
{
|
||
// we want to write this to the string
|
||
text[( i % entries_line )] = buffer[i];
|
||
}
|
||
else
|
||
{
|
||
text[( i % entries_line )] = '.';
|
||
|
||
}
|
||
if( i % entries_line == entries_line - 1 )
|
||
{
|
||
output << " " << text << "\n";
|
||
}
|
||
}
|
||
// what if we had a partial last line, we need to figure that out
|
||
auto last_line_entry = length%entries_line;
|
||
if( last_line_entry != 0 )
|
||
{
|
||
// we need to put the number of leading spaces
|
||
output << std::setw( static_cast<int>(( entries_line - last_line_entry ) *
|
||
( entry_size + 1 ))) << "" << std::setw( 1 ) << " " << text << "\n";
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| end strutil functions
|
||
//o------------------------------------------------------------------------------------------------o
|
||
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::Exceeds( int offset, int bytelength ) const -> bool
|
||
{
|
||
auto index = offset + bytelength;
|
||
return static_cast<int>( _bytedata.size() ) < index;
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::Exceeds( int offset, int bytelength, bool expand ) -> bool
|
||
{
|
||
auto rValue = Exceeds(offset, bytelength );
|
||
if( expand && rValue )
|
||
{
|
||
this->size( offset + bytelength );
|
||
rValue = false;
|
||
}
|
||
return rValue;
|
||
}
|
||
//==================================================================================================
|
||
ByteBuffer_t::ByteBuffer_t( int size, int reserve ):_index( 0 )
|
||
{
|
||
if(( reserve > 0 ) && ( size <= 0 ))
|
||
{
|
||
_bytedata.reserve( reserve );
|
||
}
|
||
if( size > 0 )
|
||
{
|
||
_bytedata.resize( size );
|
||
}
|
||
}
|
||
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::size() const -> size_t
|
||
{
|
||
return _bytedata.size();
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::size( int value, std::uint8_t fill ) -> void
|
||
{
|
||
_bytedata.resize( value, fill );
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::index() const -> int
|
||
{
|
||
return _index;
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::index( int value ) -> void
|
||
{
|
||
_index = value;
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::raw() const -> const std::uint8_t*
|
||
{
|
||
return _bytedata.data();
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::raw() -> std::uint8_t*
|
||
{
|
||
return _bytedata.data();
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::operator[]( int index ) const -> const std::uint8_t &
|
||
{
|
||
return _bytedata[index];
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::operator[]( int index ) -> std::uint8_t &
|
||
{
|
||
return _bytedata[index];
|
||
}
|
||
//==================================================================================================
|
||
auto ByteBuffer_t::Fill( std::uint8_t value, int offset, int length ) -> void
|
||
{
|
||
if( offset < 0 )
|
||
{
|
||
offset = _index;
|
||
}
|
||
if( length < 0 )
|
||
{
|
||
length = static_cast<int>( _bytedata.size() ) - offset;
|
||
}
|
||
if( length > 0 )
|
||
{
|
||
std::fill( _bytedata.begin() + offset, _bytedata.begin() + offset + length, value );
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - ByteBuffer_t::log()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Formatted dump of a byte buffer
|
||
//o------------------------------------------------------------------------------------------------o
|
||
auto ByteBuffer_t::LogByteBuffer( std::ostream &output, radix_t radix, int entries_line ) const -> void
|
||
{
|
||
DumpByteBuffer( output, _bytedata.data(), _bytedata.size(), radix, entries_line );
|
||
}
|
||
|
||
CNetworkStuff *Network = nullptr;
|
||
|
||
#define _DEBUG_PACKET 0
|
||
|
||
fd_set conn;
|
||
fd_set all;
|
||
fd_set errsock;
|
||
|
||
void KillTrades( CChar *i );
|
||
void DoorMacro( CSocket *s );
|
||
void SysBroadcast( const std::string& txt );
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::ClearBuffers()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Sends ALL buffered data
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::ClearBuffers( void )
|
||
{
|
||
std::for_each( connClients.begin(), connClients.end(), []( CSocket *sock )
|
||
{
|
||
sock->FlushBuffer();
|
||
});
|
||
std::for_each( loggedInClients.begin(), loggedInClients.end(), []( CSocket *sock )
|
||
{
|
||
sock->FlushBuffer();
|
||
});
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::SetLastOn()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Sets the laston character member value to the current date/time
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::SetLastOn( CSocket *s )
|
||
{
|
||
assert( s != nullptr );
|
||
|
||
time_t ltime;
|
||
time( <ime );
|
||
char buffer[100];
|
||
char *t = mctime( buffer, 100, <ime );
|
||
|
||
// some ctime()s like to stick \r\n on the end, we don't want that
|
||
size_t mLen = strlen( t );
|
||
for( size_t end = mLen; end-- > 0 && isspace( t[end] ) && end < mLen; )
|
||
{
|
||
t[end] = '\0';
|
||
}
|
||
|
||
if( s->CurrcharObj() != nullptr )
|
||
{
|
||
s->CurrcharObj()->SetLastOn( t );
|
||
s->CurrcharObj()->SetLastOnSecs( static_cast<UI32>( ltime ));
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::Disconnect()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Forces player to disconnect/instalog
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::Disconnect( UOXSOCKET s )
|
||
{
|
||
SetLastOn( connClients[s] );
|
||
CChar *currChar = connClients[s]->CurrcharObj();
|
||
Console << "Client " << static_cast<UI32>( s ) << " disconnected. [Total:" << static_cast<SI32>( cwmWorldState->GetPlayersOnline() - 1 ) << "]" << myendl;
|
||
|
||
if( connClients[s]->AcctNo() != AB_INVALID_ID )
|
||
{
|
||
CAccountBlock_st &actbAccount = connClients[s]->GetAccount();
|
||
if( actbAccount.wAccountIndex != AB_INVALID_ID )
|
||
{
|
||
actbAccount.wFlags.set( AB_FLAGS_ONLINE, false );
|
||
}
|
||
}
|
||
//Instalog
|
||
if( currChar != nullptr )
|
||
{
|
||
if( !currChar->IsFree() && connClients[s] != nullptr )
|
||
{
|
||
// October 6, 2002 - Support for the onLogout event.
|
||
bool returnState = false;
|
||
std::vector<UI16> scriptTriggers = currChar->GetScriptTriggers();
|
||
for( auto i : scriptTriggers )
|
||
{
|
||
cScript *toExecute = JSMapping->GetScript( i );
|
||
if( toExecute != nullptr )
|
||
{
|
||
if( toExecute->OnLogout( connClients[s], currChar ))
|
||
{
|
||
returnState = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if( !returnState )
|
||
{
|
||
// No individual scripts handling OnLogout were found - let's check global script!
|
||
cScript *toExecute = JSMapping->GetScript( static_cast<UI16>( 0 ));
|
||
if( toExecute != nullptr )
|
||
{
|
||
toExecute->OnLogout( connClients[s], currChar);
|
||
}
|
||
}
|
||
LogOut( connClients[s] );
|
||
}
|
||
}
|
||
try
|
||
{
|
||
connClients[s]->AcctNo( AB_INVALID_ID );
|
||
connClients[s]->IdleTimeout( -1 );
|
||
connClients[s]->FlushBuffer();
|
||
connClients[s]->ClearTimers();
|
||
connClients[s]->CloseSocket();
|
||
|
||
delete connClients[s];
|
||
connClients.erase( connClients.begin() + s );
|
||
}
|
||
catch( ... )
|
||
{
|
||
Console << "| CATCH: Invalid connClients[] encountered. Ignoring." << myendl;
|
||
}
|
||
cwmWorldState->DecPlayersOnline();
|
||
WhoList->FlagUpdate();
|
||
OffList->FlagUpdate();
|
||
}
|
||
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::LogOut()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Process client logout, and process instalog settings
|
||
//|
|
||
//| Changes - Date Unknown - Added multi checked to instalog processing
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::LogOut( CSocket *s )
|
||
{
|
||
// This would probably be a good place to do the disconnect
|
||
CChar *p = s->CurrcharObj();
|
||
bool valid = false;
|
||
SI16 x = p->GetX(), y = p->GetY();
|
||
UI08 world = p->WorldNumber();
|
||
UI08 instanceId = p->GetInstanceId();
|
||
|
||
KillTrades( p );
|
||
|
||
if( p->GetCommandLevel() >= CL_CNS || p->GetAccount().wAccountIndex == 0
|
||
|| ( cwmWorldState->ServerData()->YoungPlayerSystem() && p->GetAccount().wFlags.test( AB_FLAGS_YOUNG ) && cwmWorldState->ServerData()->ExpansionCoreShardEra() >= ER_UOR ))
|
||
{
|
||
valid = true;
|
||
}
|
||
else
|
||
{
|
||
auto logoutLocs = cwmWorldState->logoutLocs;
|
||
for( size_t a = 0; a < logoutLocs.size(); ++a )
|
||
{
|
||
if( logoutLocs[a].x1 <= x && logoutLocs[a].y1 <= y && logoutLocs[a].x2 >= x && logoutLocs[a].y2 >= y && logoutLocs[a].worldNum == world && logoutLocs[a].instanceId == instanceId )
|
||
{
|
||
valid = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if( !valid )
|
||
{
|
||
CMultiObj* multi = nullptr;
|
||
if( !ValidateObject( p->GetMultiObj() ))
|
||
{
|
||
multi = FindMulti( p );
|
||
}
|
||
else
|
||
{
|
||
multi = static_cast<CMultiObj *>( p->GetMultiObj() );
|
||
}
|
||
if( ValidateObject( multi ))
|
||
{
|
||
// Check house script for onMultiLogout handling. If it returns true, instant logout is allowed
|
||
std::vector<UI16> scriptTriggers = multi->GetScriptTriggers();
|
||
for( auto scriptTrig : scriptTriggers )
|
||
{
|
||
cScript* toExecute = JSMapping->GetScript( scriptTrig );
|
||
if( toExecute != nullptr )
|
||
{
|
||
if( toExecute->OnMultiLogout( multi, p ) == 1 )
|
||
{
|
||
valid = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
//If player is holding an item on the cursor, and it was picked up off the ground, drop it back to the ground
|
||
if( s->GetCursorItem() )
|
||
{
|
||
if( s->PickupSpot() == PL_GROUND )
|
||
{
|
||
CItem *i = s->GetCursorItem();
|
||
i->SetCont( nullptr );
|
||
i->SetLocation( p->GetX(), p->GetY(), p->GetZ() );
|
||
}
|
||
}
|
||
|
||
// Iterate through list of containers opened by player
|
||
auto contsOpened = s->GetContsOpenedList();
|
||
for( const auto &iCont : contsOpened->collection() )
|
||
{
|
||
if( ValidateObject( iCont ))
|
||
{
|
||
// Remove player from container's own list of players who have previously opened it
|
||
iCont->GetContOpenedByList()->Remove( s );
|
||
}
|
||
}
|
||
s->GetContsOpenedList()->Clear(); // Then clear the socket's list of opened containers
|
||
|
||
CAccountBlock_st& actbAccount = s->GetAccount();
|
||
if( valid && !p->IsCriminal() && !p->HasStolen() && !p->IsAtWar() && !p->IsAggressor( false ))
|
||
{
|
||
if( actbAccount.wAccountIndex != AB_INVALID_ID )
|
||
{
|
||
actbAccount.dwInGame = INVALIDSERIAL;
|
||
}
|
||
p->SetTimer( tPC_LOGOUT, 0 );
|
||
s->ClearTimers();
|
||
}
|
||
else
|
||
{
|
||
if( actbAccount.wAccountIndex != AB_INVALID_ID )
|
||
{
|
||
actbAccount.dwInGame = p->GetSerial();
|
||
}
|
||
p->SetTimer( tPC_LOGOUT, BuildTimeValue( cwmWorldState->ServerData()->SystemTimer( tSERVER_LOGINTIMEOUT )));
|
||
}
|
||
|
||
s->LoginComplete( false );
|
||
actbAccount.wFlags.set( AB_FLAGS_ONLINE, false );
|
||
p->SetSocket( nullptr );
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::sockInit()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Initializes and binds sockets during startup
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::sockInit( void )
|
||
{
|
||
SI32 bcode;
|
||
|
||
cwmWorldState->SetKeepRun( true );
|
||
cwmWorldState->SetError( false );
|
||
|
||
#if PLATFORM != WINDOWS
|
||
SI32 on = 1;
|
||
#endif
|
||
|
||
a_socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
|
||
if( a_socket < 0 )
|
||
{
|
||
Console.Error( " Unable to create socket");
|
||
#if PLATFORM == WINDOWS
|
||
Console.Error( oldstrutil::format( " Code %i", WSAGetLastError() ));
|
||
#else
|
||
Console << myendl;
|
||
#endif
|
||
cwmWorldState->SetKeepRun( false );
|
||
cwmWorldState->SetError( true );
|
||
Shutdown( FATAL_UOX3_ALLOC_NETWORK );
|
||
return;
|
||
}
|
||
#if PLATFORM != WINDOWS
|
||
[[maybe_unused]] int result = setsockopt( a_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof( on ));
|
||
#endif
|
||
|
||
UI32 len_connection_addr = sizeof( struct sockaddr_in );
|
||
sockaddr_in connection;
|
||
memset(( char * )&connection, 0, len_connection_addr );
|
||
connection.sin_family = AF_INET;
|
||
connection.sin_addr.s_addr = htonl( INADDR_ANY );
|
||
connection.sin_port = htons( cwmWorldState->ServerData()->ServerPort() );
|
||
bcode = bind( a_socket, ( struct sockaddr * )&connection, len_connection_addr );
|
||
|
||
if( bcode < 0 )
|
||
{
|
||
Console.Error( oldstrutil::format( " Unable to bind socket 1 - Error code: %i", bcode ));
|
||
cwmWorldState->SetKeepRun( false );
|
||
cwmWorldState->SetError( true );
|
||
Shutdown( FATAL_UOX3_ALLOC_NETWORK );
|
||
return;
|
||
}
|
||
|
||
unsigned long mode = 1;
|
||
// set the socket to nonblocking
|
||
ioctlsocket( a_socket, FIONBIO, &mode );
|
||
|
||
listen( a_socket, 42 );
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::SockClose()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Closes all sockets for shutdown
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::SockClose( void )
|
||
{
|
||
closesocket( a_socket );
|
||
std::for_each( connClients.begin(), connClients.end(), []( CSocket *sock )
|
||
{
|
||
sock->CloseSocket();
|
||
});
|
||
}
|
||
|
||
#if PLATFORM != WINDOWS
|
||
#ifdef FD_SETSIZE
|
||
#if FD_SETSIZE < 256
|
||
#undef FD_SETSIZE
|
||
#define FD_SETSIZE 256
|
||
#endif
|
||
#else
|
||
#define FD_SETSIZE 256
|
||
#endif
|
||
#endif
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::CheckConn()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Checks for connection requests
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::CheckConn( void )
|
||
{
|
||
FD_ZERO( &conn );
|
||
FD_SET( a_socket, &conn );
|
||
SI32 nfds = a_socket + 1;
|
||
SI32 s = select( nfds, &conn, nullptr, nullptr, &cwmWorldState->uoxTimeout );
|
||
if( s > 0 )
|
||
{
|
||
SI32 len = sizeof( struct sockaddr_in );
|
||
SI32 newClient;
|
||
#if PLATFORM == WINDOWS
|
||
newClient = accept( a_socket, ( struct sockaddr * )&client_addr, &len );
|
||
#else
|
||
newClient = accept( a_socket, ( struct sockaddr * )&client_addr, ( socklen_t * )&len );
|
||
if( newClient >= FD_SETSIZE )
|
||
{
|
||
Console.Error( "accept() returning unselectable fd!" );
|
||
closesocket( static_cast<UOXSOCKET>( newClient ));
|
||
return;
|
||
}
|
||
#endif
|
||
CSocket *toMake = new CSocket( newClient );
|
||
// set the ip address of the client;
|
||
toMake->ipaddress = Ip4Addr_st( client_addr.sin_addr.s_addr );
|
||
|
||
if( newClient < 0 )
|
||
{
|
||
#if PLATFORM == WINDOWS
|
||
SI32 errorCode = WSAGetLastError();
|
||
if( errorCode == WSAEWOULDBLOCK )
|
||
#else
|
||
SI32 errorCode = errno;
|
||
if( errorCode == EWOULDBLOCK )
|
||
#endif
|
||
{
|
||
delete toMake;
|
||
return;
|
||
}
|
||
Console.Error( "Error at client connection!" );
|
||
cwmWorldState->SetKeepRun( true );
|
||
cwmWorldState->SetError( true );
|
||
delete toMake;
|
||
return;
|
||
}
|
||
// April 5, 2000
|
||
// Due to an attack on the shard, and the true inability to determine who did what
|
||
// I am implementing a ShitList, any IP on this list will be immediatly dropped.
|
||
// This is a light weight firewall attempt to stop some people that are attacking
|
||
// a shard that they can easily create new accounts automatically on.
|
||
// ListFormat: x.x.x.x --> * means any for that domain class
|
||
// _ _ _
|
||
UI08 part[4];
|
||
part[0] = static_cast<UI08>( client_addr.sin_addr.s_addr & 0xFF );
|
||
part[1] = static_cast<UI08>(( client_addr.sin_addr.s_addr & 0xFF00 ) >> 8 );
|
||
part[2] = static_cast<UI08>(( client_addr.sin_addr.s_addr & 0xFF0000 ) >> 16 );
|
||
part[3] = static_cast<UI08>(( client_addr.sin_addr.s_addr & 0xFF000000 ) >> 24 );
|
||
|
||
if( IsFirewallBlocked( part ))
|
||
{
|
||
messageLoop << oldstrutil::format( "FIREWALL: Blocking address %i.%i.%i.%i --> Blocked!", part[0], part[1], part[2], part[3] );
|
||
closesocket( static_cast<UOXSOCKET>( newClient ));
|
||
delete toMake;
|
||
return;
|
||
}
|
||
//Firewall-messages are really only needed when firewall blocks, not when it lets someone through. Leads to information overload in console. Commenting out.
|
||
|
||
messageLoop << oldstrutil::format( "Client %zu [%i.%i.%i.%i] connected [Total:%lu]", cwmWorldState->GetPlayersOnline(), part[0], part[1], part[2], part[3], cwmWorldState->GetPlayersOnline() + 1 );
|
||
loggedInClients.push_back( toMake );
|
||
toMake->ClientIP( client_addr.sin_addr.s_addr );
|
||
return;
|
||
}
|
||
if( s < 0 )
|
||
{
|
||
Console.Error( " Select (Conn) failed!" );
|
||
cwmWorldState->SetKeepRun( false );
|
||
cwmWorldState->SetError( true );
|
||
return;
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::IsFirewallBlocked()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Checks if IP of connecting client is blocked by firewall
|
||
//o------------------------------------------------------------------------------------------------o
|
||
bool CNetworkStuff::IsFirewallBlocked( UI08 part[4] )
|
||
{
|
||
bool match[4];
|
||
for( size_t i = 0; i < slEntries.size(); ++i )
|
||
{
|
||
for( UI08 k = 0; k < 4; ++k )
|
||
{
|
||
if( slEntries[i].b[k] == -1 )
|
||
{
|
||
match[k] = true;
|
||
}
|
||
else
|
||
{
|
||
match[k] = ( slEntries[i].b[k] == part[k] );
|
||
}
|
||
}
|
||
if( match[0] && match[1] && match[2] && match[3] )
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
CNetworkStuff::~CNetworkStuff()
|
||
{
|
||
size_t i = 0;
|
||
size_t s = 0;
|
||
for( i = 0; i < loggedInClients.size(); ++i )
|
||
{
|
||
loggedInClients[i]->FlushBuffer();
|
||
loggedInClients[i]->CloseSocket();
|
||
delete loggedInClients[i];
|
||
}
|
||
for( i = 0; i < connClients.size(); ++i )
|
||
{
|
||
connClients[i]->FlushBuffer();
|
||
connClients[i]->CloseSocket();
|
||
s = std::max( s, connClients[i]->CliSocket() + 1 );
|
||
delete connClients[i];
|
||
}
|
||
|
||
loggedInClients.resize( 0 );
|
||
connClients.resize( 0 );
|
||
closesocket( static_cast<UOXSOCKET>( s ));
|
||
#if PLATFORM == WINDOWS
|
||
WSACleanup();
|
||
#endif
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::CheckMessage()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Check for messages from the clients
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::CheckMessage( void )
|
||
{
|
||
FD_ZERO( &all );
|
||
FD_ZERO( &errsock );
|
||
SI32 nfds = 0;
|
||
for( auto &tSock : connClients )
|
||
{
|
||
auto clientSock = static_cast<UOXSOCKET>( tSock->CliSocket() );
|
||
FD_SET( clientSock, &all );
|
||
FD_SET( clientSock, &errsock );
|
||
if( static_cast<int>( clientSock ) + 1 > nfds )
|
||
{
|
||
nfds = clientSock + 1;
|
||
}
|
||
}
|
||
SI32 s = select( nfds, &all, nullptr, &errsock, &cwmWorldState->uoxTimeout );
|
||
if( s > 0 )
|
||
{
|
||
size_t oldnow = cwmWorldState->GetPlayersOnline();
|
||
for( size_t i = 0; i < cwmWorldState->GetPlayersOnline(); ++i )
|
||
{
|
||
if( FD_ISSET( connClients[i]->CliSocket(), &errsock ))
|
||
{
|
||
Disconnect( static_cast<UOXSOCKET>( i ));
|
||
}
|
||
if(( FD_ISSET( connClients[i]->CliSocket(), &all )) && ( oldnow == cwmWorldState->GetPlayersOnline() ))
|
||
{
|
||
try
|
||
{
|
||
GetMsg( static_cast<UOXSOCKET>( i ));
|
||
}
|
||
catch( socket_error& blah )
|
||
{
|
||
#if PLATFORM != WINDOWS
|
||
Console << "Client disconnected" << myendl;
|
||
#else
|
||
if( blah.ErrorNumber() == WSAECONNRESET )
|
||
{
|
||
Console << "Client disconnected" << myendl;
|
||
}
|
||
else if( blah.ErrorNumber() != -1 )
|
||
{
|
||
Console << "Socket error: " << static_cast<SI32>( blah.ErrorNumber() ) << myendl;
|
||
}
|
||
#endif
|
||
Disconnect( static_cast<UOXSOCKET>( i )); // abnormal error
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if( s == -1 )
|
||
{
|
||
}
|
||
}
|
||
|
||
CNetworkStuff::CNetworkStuff() : peakConnectionCount( 0 ) // Initialize sockets
|
||
{
|
||
}
|
||
auto CNetworkStuff::Startup() -> void
|
||
{
|
||
FD_ZERO( &conn );
|
||
sockInit();
|
||
LoadFirewallEntries();
|
||
}
|
||
CSocket *CNetworkStuff::GetSockPtr( UOXSOCKET s )
|
||
{
|
||
if( static_cast<unsigned>(s) >= connClients.size() )
|
||
return nullptr;
|
||
|
||
return connClients[s];
|
||
}
|
||
|
||
CPInputBuffer *WhichPacket( UI08 packetId, CSocket *s );
|
||
CPInputBuffer *WhichLoginPacket( UI08 packetId, CSocket *s );
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::GetMsg()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Receive message from client
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::GetMsg( UOXSOCKET s )
|
||
{
|
||
if( static_cast<unsigned>(s) >= connClients.size() )
|
||
return;
|
||
|
||
CSocket *mSock = connClients[s];
|
||
|
||
if( mSock == nullptr )
|
||
return;
|
||
|
||
if( mSock->NewClient() )
|
||
{
|
||
SI32 count = mSock->Receive( 4 );
|
||
if( mSock->Buffer()[0] == 0x21 && count < 4 ) // UOMon
|
||
{
|
||
SI32 ho, mi, se, total;
|
||
total = ( cwmWorldState->GetUICurrentTime() - cwmWorldState->GetStartTime() ) / 1000;
|
||
ho = total / 3600;
|
||
total -= ho * 3600;
|
||
mi = total / 60;
|
||
total -= mi * 60;
|
||
se = total;
|
||
|
||
auto uoxmonitor = oldstrutil::format( "UOX LoginServer\r\nUOX3 Server: up for %ih %im %is\r\n", ho, mi, se );
|
||
mSock->Send( uoxmonitor.c_str(), static_cast<SI32>( uoxmonitor.size() ));
|
||
mSock->NewClient( false );
|
||
}
|
||
else
|
||
{
|
||
mSock->NewClient( false );
|
||
if( mSock->GetDWord( 0 ) == 0x12345678 )
|
||
{
|
||
Disconnect( s );
|
||
}
|
||
mSock->FirstPacket( true );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mSock->InLength( 0 );
|
||
CChar *ourChar = mSock->CurrcharObj();
|
||
UI08 *buffer = mSock->Buffer();
|
||
if( mSock->Receive( 1, false ) > 0 )
|
||
{
|
||
SI32 book;
|
||
UI08 packetId = buffer[0];
|
||
if( mSock->FirstPacket() && packetId != 0x80 && packetId != 0x91 )
|
||
{
|
||
// April 5, 2004 - There is a great chance that alot of the times this will be UOG2 connecting to get information from the server
|
||
if( cwmWorldState->ServerData()->ServerUOGEnabled() )
|
||
{
|
||
Console << "UOG Stats request completed. Disconnecting client. [" << static_cast<SI32>( mSock->ClientIP4() ) << "." << static_cast<SI32>( mSock->ClientIP3() ) << "." << static_cast<SI32>( mSock->ClientIP2() ) << "." << static_cast<SI32>( mSock->ClientIP1() ) << "]" << myendl;
|
||
}
|
||
else
|
||
{
|
||
Console << "Encrypted client attempting to cut in, disconnecting them: IP " << static_cast<SI32>( mSock->ClientIP1() ) << "." << static_cast<SI32>( mSock->ClientIP2() ) << "." << static_cast<SI32>( mSock->ClientIP3() ) << "." << static_cast<SI32>( mSock->ClientIP4() ) << myendl;
|
||
}
|
||
Disconnect( s );
|
||
return;
|
||
}
|
||
#if _DEBUG_PACKET
|
||
Console.Print( oldstrutil::format( "Packet ID: 0x%x\n", packetId ));
|
||
#endif
|
||
if( packetId != 0x73 && packetId != 0xA4 && packetId != 0x80 && packetId != 0x91 )
|
||
{
|
||
if( !mSock->ForceOffline() ) //Don't refresh idle-timer if character is being forced offline
|
||
{
|
||
mSock->IdleTimeout( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_LOGINTIMEOUT ));
|
||
}
|
||
}
|
||
|
||
bool doSwitch = true;
|
||
if( cwmWorldState->ServerData()->ServerOverloadPackets() )
|
||
{
|
||
std::map<UI16, UI16>::const_iterator pFind = packetOverloads.find( packetId );
|
||
if( pFind != packetOverloads.end() )
|
||
{
|
||
cScript *pScript = JSMapping->GetScript( pFind->second );
|
||
if( pScript != nullptr )
|
||
{
|
||
doSwitch = !pScript->OnPacketReceive( mSock, packetId );
|
||
}
|
||
}
|
||
}
|
||
if( doSwitch )
|
||
{
|
||
CPInputBuffer *test = WhichPacket( packetId, mSock );
|
||
if( test != nullptr )
|
||
{
|
||
mSock->ReceiveLogging( test );
|
||
if( test->Handle() )
|
||
{
|
||
doSwitch = false;
|
||
}
|
||
delete test;
|
||
}
|
||
}
|
||
if( doSwitch )
|
||
{
|
||
size_t j = 0;
|
||
switch( packetId )
|
||
{
|
||
case 0x01: // Main Menu on the character select screen
|
||
Disconnect( s );
|
||
break;
|
||
case 0x72:// Combat Mode
|
||
{
|
||
mSock->Receive( 5 );
|
||
|
||
std::vector<UI16> scriptTriggers = ourChar->GetScriptTriggers();
|
||
for( auto scriptTrig : scriptTriggers )
|
||
{
|
||
cScript *toExecute = JSMapping->GetScript( scriptTrig );
|
||
if( toExecute != nullptr )
|
||
{
|
||
if( toExecute->OnWarModeToggle( ourChar ) == 0 )
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// No individual scripts handling OnWarModeToggle returned true - let's check global script!
|
||
cScript *toExecute = JSMapping->GetScript( static_cast<UI16>( 0 ));
|
||
if( toExecute != nullptr )
|
||
{
|
||
if( toExecute->OnWarModeToggle( ourChar ) == 0 )
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
ourChar->SetWar( buffer[1] != 0 );
|
||
ourChar->SetPassive( buffer[1] == 0 ); // Set player as passive if they tab out of combat
|
||
|
||
if( ourChar->GetTimer( tCHAR_TIMEOUT ) <= cwmWorldState->GetUICurrentTime() )
|
||
{
|
||
ourChar->SetTimer( tCHAR_TIMEOUT, BuildTimeValue( 1 ));
|
||
}
|
||
else
|
||
{
|
||
ourChar->SetTimer( tCHAR_TIMEOUT, ourChar->GetTimer( tCHAR_TIMEOUT ) + 1000 );
|
||
}
|
||
mSock->Send( buffer, 5 );
|
||
#if defined( _MSC_VER )
|
||
#pragma note( "Flush location" )
|
||
#endif
|
||
mSock->FlushBuffer();
|
||
Movement->CombatWalk( ourChar );
|
||
Effects->DoSocketMusic( mSock );
|
||
ourChar->BreakConcentration( mSock );
|
||
break;
|
||
}
|
||
case 0x12:// Ext. Command
|
||
mSock->Receive( 3 );
|
||
mSock->Receive( mSock->GetWord( 1 ));
|
||
ourChar->BreakConcentration( mSock );
|
||
if( buffer[3] == 0xC7 ) // Action
|
||
{
|
||
if( ourChar->IsOnHorse() || ourChar->IsFlying() )
|
||
return; // can't bow or salute on horse or while flying
|
||
|
||
if( std::string( reinterpret_cast<char*>( &buffer[4] )) == "bow" )
|
||
{
|
||
if( ourChar->GetBodyType() == BT_GARGOYLE
|
||
|| ( cwmWorldState->ServerData()->ForceNewAnimationPacket()
|
||
&& ( ourChar->GetSocket() == nullptr || ourChar->GetSocket()->ClientType() >= CV_SA2D )))
|
||
{
|
||
// If gargoyle, human or elf, and new animation packet is enabled
|
||
Effects->PlayNewCharacterAnimation( ourChar, N_ACT_EMOTE, S_ACT_EMOTE_BOW );
|
||
}
|
||
else if(( ourChar->GetBodyType() == BT_HUMAN || ourChar->GetBodyType() == BT_ELF )
|
||
&& !cwmWorldState->ServerData()->ForceNewAnimationPacket() )
|
||
{
|
||
// If human or elf, and new animation packet is disabled
|
||
Effects->PlayCharacterAnimation( ourChar, ACT_EMOTE_BOW, 0, 5 ); // 0x20
|
||
}
|
||
else
|
||
{
|
||
// If polymorphed to other creatures
|
||
Effects->PlayCharacterAnimation( ourChar, 0x12, 0, 5 ); // Monster fidget 1
|
||
}
|
||
}
|
||
if( std::string( reinterpret_cast<char*>( &buffer[4] )) == "salute" )
|
||
{
|
||
if( ourChar->GetBodyType() == BT_GARGOYLE
|
||
|| ( cwmWorldState->ServerData()->ForceNewAnimationPacket()
|
||
&& ( ourChar->GetSocket() == nullptr || ourChar->GetSocket()->ClientType() >= CV_SA2D )))
|
||
{
|
||
// If gargoyle, human or elf, and new animation packet is enabled
|
||
Effects->PlayNewCharacterAnimation( ourChar, N_ACT_EMOTE, S_ACT_EMOTE_SALUTE );
|
||
}
|
||
else if(( ourChar->GetBodyType() == BT_HUMAN || ourChar->GetBodyType() == BT_ELF )
|
||
&& !cwmWorldState->ServerData()->ForceNewAnimationPacket() )
|
||
{
|
||
// If human or elf, and new animation packet is disabled
|
||
Effects->PlayCharacterAnimation( ourChar, ACT_EMOTE_SALUTE, 0, 5 ); // 0x21
|
||
}
|
||
else
|
||
{
|
||
// If polymorphed to other creatures
|
||
Effects->PlayCharacterAnimation( ourChar, 0x11, 0, 5 ); // Monster fidget 2
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
else if( buffer[3] == 0x58 )
|
||
{
|
||
DoorMacro( mSock );
|
||
break;
|
||
}
|
||
else if( buffer[3] == 0x24 ) // Skill
|
||
{
|
||
j = 4;
|
||
while( buffer[j] != ' ' )
|
||
{
|
||
++j;
|
||
}
|
||
buffer[j] = 0;
|
||
Skills->SkillUse( mSock, static_cast<UI08>( std::stoul( std::string(( char* )&buffer[4] ))));
|
||
break;
|
||
}
|
||
else if( buffer[3] == 0x27 || buffer[3] == 0x56 ) // Spell
|
||
{
|
||
// This'll find our spellbook for us
|
||
CItem *sBook = FindItemOfType( ourChar, IT_SPELLBOOK );
|
||
CItem *p = ourChar->GetPackItem();
|
||
if( ValidateObject( sBook ))
|
||
{
|
||
bool validLoc = false;
|
||
if( sBook->GetCont() == ourChar )
|
||
{
|
||
validLoc = true;
|
||
}
|
||
else if( ValidateObject( p ) && sBook->GetCont() == p )
|
||
{
|
||
validLoc = true;
|
||
}
|
||
|
||
if( validLoc )
|
||
{
|
||
book = buffer[4] - 0x30;
|
||
if( buffer[5] > 0x20 )
|
||
{
|
||
book = ( book * 10 ) + ( buffer[5] - 0x30 );
|
||
}
|
||
if( Magic->CheckBook((( book - 1 ) / 8 ) + 1, ( book - 1 ) % 8, sBook ))
|
||
{
|
||
if( ourChar->IsFrozen() )
|
||
{
|
||
if( ourChar->IsCasting() )
|
||
{
|
||
mSock->SysMessage( 762 ); // You are already casting a spell.
|
||
}
|
||
else
|
||
{
|
||
mSock->SysMessage( 763 ); // You cannot cast spells while frozen.
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mSock->CurrentSpellType( 0 );
|
||
Magic->SelectSpell( mSock, book );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mSock->SysMessage( 764 ); // You do not have that spell.
|
||
}
|
||
}
|
||
else
|
||
{
|
||
mSock->SysMessage( 765 ); // To cast spells, your spellbook must be in your hands or in the first layer of your pack.
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if( buffer[2] == 0x05 && buffer[3] == 0x43 ) // Open spell book
|
||
{
|
||
Magic->SpellBook( mSock );
|
||
}
|
||
break;
|
||
}
|
||
break;
|
||
// client sends them out if the title and/or author gets changed on a writeable book
|
||
// its NOT send (anymore?) when a book is closed as many packet docus state.
|
||
// 7-dec 1999
|
||
case 0x93: // Old Book Packet, no longer sent by the client
|
||
mSock->Receive( 99 );
|
||
break;
|
||
case 0x69:// Client text change
|
||
mSock->Receive( 3 );// What a STUPID message... It would be useful if
|
||
mSock->Receive( mSock->GetWord( 1 )); // it included the color changed to, but it doesn't!
|
||
break;
|
||
case 0xB5: // Open Chat Window
|
||
mSock->Receive( 64 );
|
||
break;
|
||
case 0xB6: // T2A Popuphelp request
|
||
mSock->Receive( 9 );
|
||
break;
|
||
case 0xB8: // T2A Profile request
|
||
mSock->Receive( 3 );
|
||
mSock->Receive( mSock->GetWord( 1 )); // this is how it's really done
|
||
|
||
if( buffer[3] == 1 ) // Set profile, not feasible yet
|
||
{
|
||
mSock->SysMessage( 768 ); // You cannot set your profile currently.
|
||
}
|
||
else
|
||
{
|
||
SERIAL targSerial = mSock->GetDWord( 4 );
|
||
CChar *player = CalcCharObjFromSer( targSerial );
|
||
if( !ValidateObject( player ))
|
||
{
|
||
mSock->SysMessage( 769 ); // This player no longer exists.
|
||
}
|
||
else
|
||
{
|
||
// Still doesn't work
|
||
// Request Char Profile (Variable # of bytes)
|
||
// BYTE cmd
|
||
// BYTE[2] blockSize
|
||
// BYTE mode (CLIENT ONLY! Does not exist in server message.)
|
||
// BYTE[4] id
|
||
// If request, ends here.
|
||
// If Update request
|
||
// BYTE[2] cmdType (0x0001 <20> Update)
|
||
// BYTE[2] msglen (# of unicode characters)
|
||
// BYTE[msglen][2] new profile, in unicode, not null terminated.
|
||
// Else If from server
|
||
// BYTE[?] title (not unicode, null terminated.)
|
||
// BYTE[?][2] static profile ( unicode string can't be edited )
|
||
// BYTE[2] (0x0000) (static profile terminator)
|
||
// BYTE[?][2] profile (in unicode, can be edited)
|
||
// BYTE[2] (0x0000) (profile terminator)
|
||
|
||
UI08 tempBuffer[1024];
|
||
std::string ourMessage;
|
||
memset( tempBuffer, 0, sizeof( tempBuffer[0] ) * 1024 ); // zero it out
|
||
tempBuffer[0] = 0xB8;
|
||
tempBuffer[3] = buffer[4];
|
||
tempBuffer[4] = buffer[5];
|
||
tempBuffer[5] = buffer[6];
|
||
tempBuffer[6] = buffer[7];
|
||
auto playerName = player->GetNameRequest( mSock->CurrcharObj(), NRS_JOURNAL );
|
||
size_t plNameLen = playerName.size();
|
||
strncopy(( char * )&tempBuffer[7], 30, playerName.c_str(), 30 );
|
||
size_t mj = 8 + plNameLen;
|
||
auto playerTitle = player->GetTitle();
|
||
size_t plTitleLen = playerTitle.size() + 1;
|
||
for( size_t k = 0; k < plTitleLen; ++k )
|
||
{
|
||
tempBuffer[mj++] = 0;
|
||
tempBuffer[mj++] = playerTitle.data()[k];
|
||
}
|
||
mj += 2;
|
||
ourMessage = "Test of Char Profile";
|
||
for( j = 0; j < ourMessage.size(); ++j )
|
||
{
|
||
tempBuffer[mj++] = 0;
|
||
tempBuffer[mj++] = ourMessage[j];
|
||
}
|
||
mj += 2;
|
||
tempBuffer[2] = static_cast<UI08>( mj );
|
||
mSock->Send( tempBuffer, static_cast<SI32>( mj ));
|
||
#if defined( _MSC_VER )
|
||
#pragma note( "Flush location" )
|
||
#endif
|
||
mSock->FlushBuffer();
|
||
}
|
||
}
|
||
break;
|
||
case 0xBB:
|
||
// Ultima Messenger (9 bytes)
|
||
// BYTE cmd
|
||
// BYTE[4] id1
|
||
// BYTE[4] id2
|
||
// Note: This is both a client and server message.
|
||
mSock->Receive( 9 );
|
||
break;
|
||
case 0x3A: // skills management packet
|
||
mSock->Receive( 6 ); // it's always 6 so Westy tells me... let's not be intelligent =)
|
||
UI16 skillNum;
|
||
skillNum = mSock->GetWord( 3 );
|
||
ourChar->SetSkillLock( static_cast<SkillLock>( buffer[5] ), static_cast<UI08>( skillNum ));
|
||
break;
|
||
case 0x56:
|
||
Console << "'Plot Course' button on a map clicked." << myendl;
|
||
break;
|
||
case 0xD0:
|
||
// Configuration file
|
||
mSock->Receive( 3 );
|
||
mSock->Receive( mSock->GetWord( 1 ));
|
||
break;
|
||
case 0xD9: // Spy On Client
|
||
break;
|
||
case 0xFB: // Update View Public House Contents
|
||
mSock->Receive( 2 );
|
||
break;
|
||
case 0xF0: // ClassicUO Map Tracker, handled by CPIKrriosClientSpecial in CPacketReceive.cpp
|
||
break;
|
||
default:
|
||
FD_ZERO( &all );
|
||
FD_SET( mSock->CliSocket(), &all );
|
||
SI32 nfds;
|
||
nfds = static_cast<int>( mSock->CliSocket() ) + 1;
|
||
if( select( nfds, &all, nullptr, nullptr, &cwmWorldState->uoxTimeout ) > 0 )
|
||
{
|
||
mSock->Receive( MAXBUFFER );
|
||
}
|
||
|
||
Console << oldstrutil::format( "Unknown message from client: 0x%02X - Ignored", packetId ) << myendl;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else // Sortta error type thing, they disconnected already
|
||
{
|
||
Disconnect( s );
|
||
}
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::CheckLoginMessage()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Check for messages from the clients
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::CheckLoginMessage( void )
|
||
{
|
||
fd_set all;
|
||
fd_set errsock;
|
||
size_t i;
|
||
|
||
cwmWorldState->uoxTimeout.tv_sec = 0;
|
||
cwmWorldState->uoxTimeout.tv_usec = 1;
|
||
|
||
FD_ZERO( &all );
|
||
FD_ZERO( &errsock );
|
||
|
||
SI32 nfds = 0;
|
||
for( i = 0; i < loggedInClients.size(); ++i )
|
||
{
|
||
size_t clientSock = loggedInClients[i]->CliSocket();
|
||
FD_SET( clientSock, &all );
|
||
FD_SET( clientSock, &errsock );
|
||
if( static_cast<int>( clientSock ) + 1 > nfds )
|
||
{
|
||
nfds = static_cast<int>( clientSock ) + 1;
|
||
}
|
||
}
|
||
SI32 s = select( static_cast<UOXSOCKET>( nfds ), &all, nullptr, &errsock, &cwmWorldState->uoxTimeout );
|
||
if( s > 0 )
|
||
{
|
||
size_t oldnow = loggedInClients.size();
|
||
for( i = 0; i < loggedInClients.size(); ++i )
|
||
{
|
||
if( FD_ISSET( loggedInClients[i]->CliSocket(), &errsock ))
|
||
{
|
||
LoginDisconnect( static_cast<UOXSOCKET>( i ));
|
||
continue;
|
||
}
|
||
if(( FD_ISSET( loggedInClients[i]->CliSocket(), &all )) && ( oldnow == loggedInClients.size() ))
|
||
{
|
||
try
|
||
{
|
||
GetLoginMsg( static_cast<UOXSOCKET>( i ));
|
||
}
|
||
catch( socket_error& blah )
|
||
{
|
||
#if PLATFORM != WINDOWS
|
||
messageLoop << "Client disconnected";
|
||
#else
|
||
if( blah.ErrorNumber() == WSAECONNRESET )
|
||
{
|
||
messageLoop << "Client disconnected";
|
||
}
|
||
else if( blah.ErrorNumber() != -1 )
|
||
{
|
||
messageLoop << oldstrutil::format( "Socket error: %i", blah.ErrorNumber() );
|
||
}
|
||
#endif
|
||
LoginDisconnect( static_cast<UOXSOCKET>( i )); // abnormal error
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if( s == -1 )
|
||
{
|
||
#if PLATFORM == WINDOWS
|
||
SI32 errorCode = WSAGetLastError();
|
||
if( errorCode != 10022 )
|
||
{
|
||
Console << static_cast<SI32>( errorCode ) << myendl;
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::LoginDisconnect()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Force disconnection of player //Instalog
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::LoginDisconnect( UOXSOCKET s )
|
||
{
|
||
messageLoop << oldstrutil::format( "LoginClient %u disconnected.", s );
|
||
loggedInClients[s]->FlushBuffer();
|
||
loggedInClients[s]->CloseSocket();
|
||
|
||
// 9/17/01 : fix for clients disconnecting during login not being able to reconnect.
|
||
if( loggedInClients[s]->AcctNo() != AB_INVALID_ID )
|
||
{
|
||
CAccountBlock_st& actbAccount = loggedInClients[s]->GetAccount();
|
||
if( actbAccount.wAccountIndex != AB_INVALID_ID )
|
||
{
|
||
actbAccount.wFlags.set( AB_FLAGS_ONLINE, false );
|
||
}
|
||
}
|
||
|
||
delete loggedInClients[s];
|
||
loggedInClients.erase( loggedInClients.begin() + s );
|
||
}
|
||
|
||
void CNetworkStuff::LoginDisconnect( CSocket *s ) // Force disconnection of player //Instalog
|
||
{
|
||
UOXSOCKET i = FindLoginPtr( s );
|
||
LoginDisconnect( i );
|
||
}
|
||
|
||
void CNetworkStuff::Disconnect( CSocket *s ) // Force disconnection of player //Instalog
|
||
{
|
||
UOXSOCKET i = FindNetworkPtr( s );
|
||
Disconnect( i );
|
||
}
|
||
|
||
UOXSOCKET CNetworkStuff::FindLoginPtr( CSocket *s )
|
||
{
|
||
for( UOXSOCKET i = 0; static_cast<unsigned>(i) < loggedInClients.size(); ++i )
|
||
{
|
||
if( loggedInClients[i] == s )
|
||
return i;
|
||
}
|
||
return 0xFFFFFFFF;
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::Transfer()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Transfers from the logged in queue to the in world queue
|
||
//|
|
||
//| Notes - Takes something out of the logging in queue and places it in the in world queue
|
||
//| REQUIRES THREAD SAFETY
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::Transfer( CSocket *mSock )
|
||
{
|
||
UOXSOCKET s = FindLoginPtr( mSock );
|
||
if( static_cast<unsigned>(s) >= loggedInClients.size() )
|
||
return;
|
||
|
||
//std::scoped_lock lock(internallock);
|
||
connClients.push_back( loggedInClients[s] );
|
||
loggedInClients.erase( loggedInClients.begin() + s );
|
||
cwmWorldState->SetPlayersOnline( connClients.size() );
|
||
if( cwmWorldState->GetPlayersOnline() > peakConnectionCount )
|
||
{
|
||
peakConnectionCount = cwmWorldState->GetPlayersOnline();
|
||
}
|
||
return;
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::GetLoginMsg()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Gets login message from socket
|
||
//o------------------------------------------------------------------------------------------------o
|
||
void CNetworkStuff::GetLoginMsg( UOXSOCKET s )
|
||
{
|
||
CSocket *mSock = loggedInClients[s];
|
||
if( mSock == nullptr )
|
||
return;
|
||
|
||
if( mSock->NewClient() )
|
||
{
|
||
[[maybe_unused]] SI32 count;
|
||
count = mSock->Receive( 4 );
|
||
auto packetId = mSock->Buffer()[0];
|
||
|
||
if( cwmWorldState->ServerData()->ServerOverloadPackets() )
|
||
{
|
||
// Allow overloading initial connection packets
|
||
std::map<UI16, UI16>::const_iterator pFind = packetOverloads.find( packetId );
|
||
if( pFind != packetOverloads.end() )
|
||
{
|
||
cScript *pScript = JSMapping->GetScript( pFind->second );
|
||
if( pScript != nullptr )
|
||
{
|
||
pScript->OnPacketReceive( mSock, packetId );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( packetId == 0xEF || packetId == 0xC0 || packetId == 0xFF )
|
||
{
|
||
WhichLoginPacket( packetId, mSock );
|
||
//mSock->Receive( 21, false );
|
||
}
|
||
|
||
mSock->NewClient( false );
|
||
if( mSock->GetDWord( 0 ) == 0x12345678 )
|
||
{
|
||
LoginDisconnect( s );
|
||
}
|
||
mSock->FirstPacket( true );
|
||
}
|
||
else
|
||
{
|
||
mSock->InLength( 0 );
|
||
UI08 *buffer = mSock->Buffer();
|
||
if( mSock->Receive( 1, false ) > 0 )
|
||
{
|
||
UI08 packetId = buffer[0];
|
||
if( mSock->FirstPacket() && packetId != 0x80 && packetId != 0x91 && packetId != 0xE4 && packetId != 0xF1 )
|
||
{
|
||
// April 5, 2004 - Hmmm there are two of these ?
|
||
if( cwmWorldState->ServerData()->ServerUOGEnabled() )
|
||
{
|
||
messageLoop << oldstrutil::format( "UOG Stats Sent or Encrypted client detected. [%i.%i.%i.%i]", mSock->ClientIP4(), mSock->ClientIP3(), mSock->ClientIP2(), mSock->ClientIP1() );
|
||
}
|
||
else
|
||
{
|
||
messageLoop << oldstrutil::format( "Encrypted client detected. [%i.%i.%i.%i]", mSock->ClientIP4(), mSock->ClientIP3(), mSock->ClientIP2(), mSock->ClientIP1() );
|
||
}
|
||
LoginDisconnect( s );
|
||
return;
|
||
}
|
||
else if( mSock->FirstPacket() && packetId == 0 )
|
||
{
|
||
messageLoop << oldstrutil::format( "Buffer is empty, no packets to read. Disconnecting client. [%i.%i.%i.%i]", mSock->ClientIP4(), mSock->ClientIP3(), mSock->ClientIP2(), mSock->ClientIP1() );
|
||
LoginDisconnect( s );
|
||
return;
|
||
}
|
||
bool doSwitch = true;
|
||
if( cwmWorldState->ServerData()->ServerOverloadPackets() )
|
||
{
|
||
std::map<UI16, UI16>::const_iterator pFind = packetOverloads.find( packetId );
|
||
if( pFind != packetOverloads.end() )
|
||
{
|
||
cScript *pScript = JSMapping->GetScript( pFind->second );
|
||
if( pScript != nullptr )
|
||
{
|
||
doSwitch = !pScript->OnPacketReceive( mSock, packetId );
|
||
}
|
||
}
|
||
}
|
||
if( doSwitch )
|
||
{
|
||
CPInputBuffer *test = nullptr;
|
||
try
|
||
{
|
||
test = WhichLoginPacket( packetId, mSock );
|
||
}
|
||
catch( socket_error& )
|
||
{
|
||
Console.Warning( oldstrutil::format( "Bad packet request thrown! [packet ID: 0x%x]", packetId ));
|
||
mSock->FlushIncoming();
|
||
return;
|
||
}
|
||
if( test != nullptr )
|
||
{
|
||
#ifdef _DEBUG_PACKET
|
||
Console.Log( oldstrutil::format( "Logging packet ID: 0x%X", packetId ), "loginthread.txt" );
|
||
#endif
|
||
mSock->ReceiveLogging( test );
|
||
if( test->Handle() )
|
||
{
|
||
doSwitch = false;
|
||
}
|
||
delete test;
|
||
}
|
||
}
|
||
if( doSwitch )
|
||
{
|
||
switch( packetId )
|
||
{
|
||
case 0x01:
|
||
//Disconnect Notification (5 bytes)
|
||
// BYTE cmd
|
||
// BYTE[4] pattern (0xFFFFFFFF)
|
||
//Note: Client message
|
||
//Note: Sent when the user chooses to return to the main menu from the character select menu.
|
||
//Note: Since the character select menu no longer has a main menu button, this message is no longer sent.
|
||
//Note: It<49>s send again in current 3.0.x clients. When player does "paper-doll logout"
|
||
LoginDisconnect( s );
|
||
break;
|
||
case 0xBB:
|
||
// Ultima Messenger (9 bytes)
|
||
// BYTE cmd
|
||
// BYTE[4] id1
|
||
// BYTE[4] id2
|
||
//Note: This is both a client and server message.
|
||
mSock->Receive( 9 );
|
||
break;
|
||
case 0xBF:
|
||
mSock->Receive( 3 );
|
||
mSock->Receive( mSock->GetWord( 1 ));
|
||
break;
|
||
default:
|
||
SI32 nfds;
|
||
fd_set all;
|
||
FD_ZERO( &all );
|
||
FD_SET( mSock->CliSocket(), &all );
|
||
nfds = static_cast<UOXSOCKET>( mSock->CliSocket() ) + 1;
|
||
if( select( nfds, &all, nullptr, nullptr, &cwmWorldState->uoxTimeout ) > 0 )
|
||
{
|
||
mSock->Receive( MAXBUFFER );
|
||
}
|
||
messageLoop << oldstrutil::format( "Unknown message from client: 0x%02X - Ignored", packetId );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else // Sortta error type thing, they disconnected already
|
||
{
|
||
LoginDisconnect( s );
|
||
}
|
||
}
|
||
}
|
||
|
||
UOXSOCKET CNetworkStuff::FindNetworkPtr( CSocket *toFind )
|
||
{
|
||
for( UOXSOCKET i = 0; static_cast<unsigned>(i) < connClients.size(); ++i )
|
||
{
|
||
if( connClients[i] == toFind )
|
||
return i;
|
||
}
|
||
return 0xFFFFFFFF;
|
||
}
|
||
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Function - CNetworkStuff::LoadFirewallEntries()
|
||
//o------------------------------------------------------------------------------------------------o
|
||
//| Purpose - Loads list of banned IPs from firewall list
|
||
//o------------------------------------------------------------------------------------------------o
|
||
auto CNetworkStuff::LoadFirewallEntries() -> void
|
||
{
|
||
std::string token;
|
||
std::string fileToUse;
|
||
if( !FileExists( "banlist.ini" ))
|
||
{
|
||
if( FileExists( "firewall.ini" ))
|
||
{
|
||
fileToUse = "firewall.ini";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fileToUse = "banlist.ini";
|
||
}
|
||
if( !fileToUse.empty() )
|
||
{
|
||
auto firewallData = std::make_unique<Script>( fileToUse, NUM_DEFS, false );
|
||
if( firewallData )
|
||
{
|
||
SI16 p[4];
|
||
for( const auto &[secName, firewallSect] : firewallData->collection() )
|
||
{
|
||
if( firewallSect )
|
||
{
|
||
for( const auto &sec : firewallSect->collection() )
|
||
{
|
||
auto tag = sec->tag;
|
||
auto data = sec->data;
|
||
if( oldstrutil::upper( tag ) == "IP" )
|
||
{
|
||
data = oldstrutil::trim( oldstrutil::removeTrailing( data, "//" ));
|
||
if( !data.empty() )
|
||
{
|
||
auto psecs = oldstrutil::sections( data, "." );
|
||
if( psecs.size() == 4 ) // Wellformed IP address
|
||
{
|
||
for( UI08 i = 0; i < 4; ++i )
|
||
{
|
||
token = psecs[i];
|
||
if( token == "*" )
|
||
{
|
||
p[i] = -1;
|
||
}
|
||
else
|
||
{
|
||
p[i] = static_cast<SI16>( std::stoi( token, nullptr, 0 ));
|
||
}
|
||
}
|
||
slEntries.push_back( FirewallEntry_st( p[0], p[1], p[2], p[3] ));
|
||
}
|
||
else if( data != "\n" && data != "\r" )
|
||
{
|
||
Console.Error( oldstrutil::format( "Malformed IP address in banlist.ini (line: %s)", data.c_str() ));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void CNetworkStuff::RegisterPacket( UI08 packet, [[maybe_unused]] UI08 subCmd, UI16 scriptId )
|
||
{
|
||
//UI16 packetId = static_cast<UI16>(( subCmd << 8 ) + packet ); // Registration of subCmd disabled until it can be fully implemented
|
||
UI16 packetId = static_cast<UI16>( packet );
|
||
packetOverloads[packetId] = scriptId;
|
||
}
|
||
|
||
void CNetworkStuff::CheckConnections( void )
|
||
{
|
||
CheckConn();
|
||
}
|
||
|
||
void CNetworkStuff::CheckMessages( void )
|
||
{
|
||
CheckLoginMessage();
|
||
CheckMessage();
|
||
}
|
||
|
||
size_t CNetworkStuff::PeakConnectionCount( void ) const
|
||
{
|
||
return peakConnectionCount;
|
||
}
|
||
|