I've seen several people asking / talking about limiting client connections by IP address lately, so I figure I'd just post my implementation of it. Give it a try and let me know what you think.
.\common\ruletypes.h
Change:
Code:
RULE_CATEGORY( World )
RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 )
RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 )
RULE_CATEGORY_END()
To:
Code:
RULE_CATEGORY( World )
RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 )
RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 )
RULE_INT ( World, MaxClientsPerIP, -1 ) //Lieka Edit: Maximum number of clients allowed to connect per IP address. Default value: -1 (feature disabled)
RULE_INT ( World, ExemptMaxClientsStatus, -1 ) //Lieka Edit: Exempt accounts from the MaxClientsPerIP rule, if their status is >= this value. Default value: -1 (feature disabled)
RULE_CATEGORY_END()
.\world\clientlist.h
Change:
Code:
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
void CLERemoveZSRef(ZoneServer* iZS);
ClientListEntry* CheckAuth(int32 iLSID, const char* iKey);
ClientListEntry* CheckAuth(const char* iName, const char* iPassword);
ClientListEntry* CheckAuth(int32 id, const char* iKey, int32 ip);
ClientListEntry* FindCharacter(const char* name);
ClientListEntry* FindCLEByAccountID(int32 iAccID);
ClientListEntry* FindCLEByCharacterID(int32 iAccID);
ClientListEntry* GetCLE(int32 iID);
void CLCheckStale();
void CLEKeepAlive(int32 numupdates, int32* wid);
void CLEAdd(int32 iLSID, const char* iLoginName, const char* iLoginKey, sint16 iWorldAdmin = 0, int32 ip = 0, uint8 local=0);
void UpdateClientGuild(int32 char_id, int32 guild_id);
To:
Code:
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
void CLERemoveZSRef(ZoneServer* iZS);
ClientListEntry* CheckAuth(int32 iLSID, const char* iKey);
ClientListEntry* CheckAuth(const char* iName, const char* iPassword);
ClientListEntry* CheckAuth(int32 id, const char* iKey, int32 ip);
ClientListEntry* FindCharacter(const char* name);
ClientListEntry* FindCLEByAccountID(int32 iAccID);
ClientListEntry* FindCLEByCharacterID(int32 iAccID);
ClientListEntry* GetCLE(int32 iID);
void GetCLEIP(int32 iIP);
void CLCheckStale();
void CLEKeepAlive(int32 numupdates, int32* wid);
void CLEAdd(int32 iLSID, const char* iLoginName, const char* iLoginKey, sint16 iWorldAdmin = 0, int32 ip = 0, uint8 local=0);
void UpdateClientGuild(int32 char_id, int32 guild_id);
.\world\clientlist.cpp
After:
Code:
void ClientList::CLERemoveZSRef(ZoneServer* iZS) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);
iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->Server() == iZS) {
iterator.GetData()->ClearServer(); // calling this before LeavingZone() makes CLE not update the number of players in a zone
iterator.GetData()->LeavingZone();
}
iterator.Advance();
}
}
ClientListEntry* ClientList::GetCLE(int32 iID) {
LinkedListIterator<ClientListEntry*> iterator(clientlist);
iterator.Reset();
while(iterator.MoreElements()) {
if (iterator.GetData()->GetID() == iID) {
return iterator.GetData();
}
iterator.Advance();
}
return 0;
}
Add:
Code:
//Lieka Edit Begin: Check current CLE Entry IPs against incoming connection
void ClientList::GetCLEIP(int32 iIP) {
ClientListEntry* countCLEIPs = 0;
LinkedListIterator<ClientListEntry*> iterator(clientlist);
int IPInstances = 0;
iterator.Reset();
while(iterator.MoreElements()) {
countCLEIPs = iterator.GetData();
if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() <= (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) {
IPInstances++;
if (IPInstances > (RuleI(World, MaxClientsPerIP)){
countCLEIPs->SetOnline(CLE_Status_Offline);
iterator.RemoveCurrent();
}
}
iterator.Advance();
}
}
//Lieka Edit End
.\world\client.cpp
After:
Code:
case OP_EnterWorld: // Enter world
{
if (GetAccountID() == 0) {
clog(WORLD__CLIENT_ERR,"Enter world with no logged in account");
eqs->Close();
break;
}
if(GetAdmin() < 0)
{
clog(WORLD__CLIENT,"Account banned or suspended.");
eqs->Close();
break;
}
Add:
Code:
if (RuleI(World, MaxClientsPerIP) >= 0) {
client_list.GetCLEIP(this->GetIP()); //Lieka Edit Begin: Check current CLE Entry IPs against incoming connection
}
Required SQL:
Code:
Insert into rule_values values (0, 'World:MaxClientsPerIP', -1);
Insert into rule_values values (0, 'World:ExemptMaxClientsStatus', -1);
There are 2 new rule values.
World:MaxClientsPerIP = Maximum number of simultaneous EQ Client connections allowed per IP address. Set the rule value to -1 to disable this feature.
World:ExemptMaxClientsStatus = Minimum Account status to exempt the MaxClientsPerIP rule. This is helpful for the inevitable random family of 16 that live together, and all want to play on your server, as well as GMs, Devs, and QA staff. Again, set the rule value to -1 to disable this feature.
It's commented throughout, but let me know if you have questions.
Thanks,
Dax