Browse Source

1st round of backporting in LS features. Equip Appearance Enabled. Note: Bug exists see issue #433.

devn00b 1 year ago
parent
commit
57027b5d48

+ 127 - 0
EQ2/source/LoginServer/LWorld.cpp

@@ -545,6 +545,52 @@ bool LWorld::Process() {
 				Kick("Possible Hacking Attempt");
 			break;
 		}
+
+		case ServerOP_LoginEquipment: {
+			LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode %04X (%i): ServerOP_LoginEquipment", pack->opcode, pack->opcode);
+
+			pack->Inflate();
+			EquipmentUpdateList_Struct* updates = 0;
+			if(pack->size >= sizeof(EquipmentUpdateList_Struct) && ((EquipmentUpdateList_Struct*)pack->pBuffer)->total_updates <= MAX_LOGIN_APPEARANCE_COUNT){
+				updates = (EquipmentUpdateList_Struct*)pack->pBuffer;
+				EquipmentUpdate_Struct* equip = 0;
+				int32 pos = sizeof(EquipmentUpdateList_Struct);
+				sint16 num_updates = 0;
+				map<int32, LoginEquipmentUpdate> equip_updates;
+				LoginEquipmentUpdate update;
+				while(pos < pack->size && num_updates < updates->total_updates){
+					equip = (EquipmentUpdate_Struct*)(pack->pBuffer+pos);
+					update.world_char_id	= equip->world_char_id;
+					update.equip_type		= equip->equip_type;
+					update.red				= equip->red;
+					update.green			= equip->green;
+					update.blue				= equip->blue;
+					update.highlight_red	= equip->highlight_red;
+					update.highlight_green	= equip->highlight_green;
+					update.highlight_blue	= equip->highlight_blue;
+					update.slot				= equip->slot;
+					pos += sizeof(EquipmentUpdate_Struct);
+					num_updates++;
+					equip_updates[equip->id] = update; // JohnAdams: I think I need item_appearances.id from World here?
+				}
+
+				LogWrite(LOGIN__DEBUG, 1, "Login", "Processing %i Login Appearance Updates...", num_updates);
+				if(equip_updates.size() == updates->total_updates)
+				{
+					world_list.AddServerEquipmentUpdates(this, equip_updates);
+				}
+				else
+				{
+					LogWrite(LOGIN__ERROR, 0, "Login", "Error processing login appearance updates for server: %s\n\t%s, function %s, line %i", GetAccount(), __FILE__, __FUNCTION__, __LINE__);
+				}
+			}
+			else
+			{
+				LogWrite(LOGIN__ERROR, 0, "Login", "World ID '%i', Possible Hacking Attempt (func: %s, line: %i", GetAccountID(), __FUNCTION__, __LINE__);
+				Kick("Possible Hacking Attempt");
+			}
+			break;
+									  }
 		case ServerOP_BugReport:{
 			if(pack->size == sizeof(BugReport)){
 				BugReport* report = (BugReport*)pack->pBuffer;
@@ -1342,6 +1388,24 @@ void LWorldList::AddServerZoneUpdates(LWorld* world, map<int32, LoginZoneUpdate>
 	}
 	server_zone_updates.Put(server_id, updates);
 }
+//devn00b temp
+
+void LWorldList::AddServerEquipmentUpdates(LWorld* world, map<int32, LoginEquipmentUpdate> updates){
+	int32 server_id = world->GetID();
+	map<int32, LoginEquipmentUpdate>::iterator itr;
+	for(itr = updates.begin(); itr != updates.end(); itr++){
+		LogWrite(MISC__TODO, 1, "TODO", "JA: Until we learn what this does, can't risk worlds being kicked performing login appearance updates...\n%s, func: %s, line: %i", __FILE__, __FUNCTION__, __LINE__);
+
+		/*if(equip_updates_already_used.size() >= 1500 || equip_updates_already_used[server_id].count(itr->first) > 0)
+		{
+			LogWrite(LOGIN__ERROR, 0, "Login", "World ID '%i': Hacking attempt. (function: %s, line: %i", world->GetAccountID(), __FUNCTION__, __LINE__);
+			world->Kick("Hacking attempt.");
+			return;
+		}*/
+		equip_updates_already_used[server_id][itr->first] = true;
+	}
+	server_equip_updates.Put(server_id, updates);
+}
 
 void LWorldList::RequestServerUpdates(LWorld* world){
 	if(world){
@@ -1385,9 +1449,72 @@ void LWorldList::ProcessServerUpdates(){
 			}
 		}
 	}
+	ProcessLSEquipUpdates();
+	MWorldMap.releasereadlock();
+
+
+}
+void LWorldList::RequestServerEquipUpdates(LWorld* world)
+{
+	if(world)
+	{
+		ServerPacket *pack_equip = new ServerPacket(ServerOP_LoginEquipment, sizeof(EquipmentUpdateRequest_Struct));
+		EquipmentUpdateRequest_Struct *request_equip = (EquipmentUpdateRequest_Struct *)pack_equip->pBuffer;
+		request_equip->max_per_batch = MAX_LOGIN_APPEARANCE_COUNT; // item appearance data smaller, request more at a time?
+		LogWrite(LOGIN__DEBUG, 1, "Login", "Sending equipment update requests to world: (%s)... (Batch Size: %i)", world->GetName(), request_equip->max_per_batch);
+		world->SendPacket(pack_equip);
+		delete pack_equip;
+		equip_update_timeouts.Put(world->GetID(), Timer::GetCurrentTime2() + 30000);
+	}
+}
+void LWorldList::ProcessLSEquipUpdates()
+{
+	// process login_equipment updates
+	MutexMap<int32, map<int32, LoginEquipmentUpdate> >::iterator itr_equip = server_equip_updates.begin();
+	while(itr_equip.Next())
+	{
+		if(itr_equip->second.size() > 0)
+		{
+			LogWrite(LOGIN__DEBUG, 1, "Login", "Setting Login Appearances...");
+			database.SetServerEquipmentAppearances(itr_equip->first, itr_equip->second);
+			if(itr_equip->second.size() == MAX_LOGIN_APPEARANCE_COUNT)			
+				awaiting_equip_update.Put(itr_equip->first, Timer::GetCurrentTime2() + 10000); //only process 100 updates in a 10 second period to avoid network problems
+			server_equip_updates.erase(itr_equip->first);
+		}
+		if(equip_update_timeouts.count(itr_equip->first) == 0 || equip_update_timeouts.Get(itr_equip->first) <= Timer::GetCurrentTime2())
+		{
+			LogWrite(LOGIN__DEBUG, 1, "Login", "Clearing Login Appearances Update Timers...");
+			equip_update_timeouts.erase(itr_equip->first);
+			server_equip_updates.erase(itr_equip->first);
+		}
+	}
+	LWorld* world = 0;
+	MWorldMap.readlock();
+	map<int32, LWorld*>::iterator map_itr;
+	for(map_itr = worldmap.begin(); map_itr != worldmap.end(); map_itr++)
+	{
+		world = map_itr->second;
+		if(world && world->GetID())
+		{
+			if(last_equip_updated.count(world) == 0 || last_equip_updated.Get(world) <= Timer::GetCurrentTime2())
+			{
+				LogWrite(LOGIN__DEBUG, 1, "Login", "Clearing Login Appearances Update Counters...");
+				equip_updates_already_used[world->GetID()].clear();
+				RequestServerEquipUpdates(world);
+				last_equip_updated.Put(world, Timer::GetCurrentTime2() + 900000); // every 15 mins
+			}
+			if( awaiting_equip_update.count(world->GetID()) > 0 && awaiting_equip_update.Get(world->GetID()) <= Timer::GetCurrentTime2())
+			{
+				LogWrite(LOGIN__DEBUG, 1, "Login", "Erase awaiting equip updates...");
+				awaiting_equip_update.erase(world->GetID());
+				RequestServerEquipUpdates(world);
+			}
+		}
+	}
 	MWorldMap.releasereadlock();
 }
 
+
 ThreadReturnType ServerUpdateLoop(void* tmp) {
 #ifdef WIN32
 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

+ 15 - 0
EQ2/source/LoginServer/LWorld.h

@@ -33,6 +33,7 @@
 #include "client.h"
 
 #define MAX_UPDATE_COUNT	20
+#define MAX_LOGIN_APPEARANCE_COUNT	100
 
 #ifdef WIN32
 	void ServerUpdateLoop(void* tmp);
@@ -182,6 +183,10 @@ public:
 	bool	WriteXML();
 
 	void	ListWorldsToConsole();
+	//devn00b temp
+	void	AddServerEquipmentUpdates(LWorld* world, map<int32, LoginEquipmentUpdate> updates);
+	void	ProcessLSEquipUpdates();
+	void	RequestServerEquipUpdates(LWorld* world);
 
 	void	SetUpdateServerList ( bool var ) { UpdateServerList = var; }
 	bool	ContinueServerUpdates(){ return server_update_thread; }
@@ -211,6 +216,16 @@ private:
 	TCPServer*				tcplistener;
 	TCPConnection*			OutLink;
 
+	//devn00b temp
+	// JohnAdams: login appearances, copied from above
+	map<int32, map<int32, bool> > equip_updates_already_used;
+	MutexMap<int32, int32> equip_update_timeouts;
+	MutexMap<int32, int32> awaiting_equip_update;
+	MutexMap<LWorld*, int32> last_equip_updated;
+	MutexMap<int32, map<int32, LoginEquipmentUpdate> > server_equip_updates;
+	//
+	///
+
 	// holds the world server list so we don't have to create it for every character
 	// logging in
 	map<int32,EQ2Packet*> ServerListData;

+ 74 - 3
EQ2/source/LoginServer/LoginDatabase.cpp

@@ -7,6 +7,8 @@
 #include "../common/debug.h"
 
 #include <iostream>
+#include <sstream>
+#include <iomanip>
 using namespace std;
 
 #ifdef WIN32
@@ -21,6 +23,7 @@ using namespace std;
 #endif
 
 #include "../common/Log.h"
+#include "../common/DatabaseNew.h"
 #include "LoginDatabase.h"
 #include "LoginAccount.h"
 #include "../common/MiscFunctions.h"
@@ -32,6 +35,16 @@ extern LoginDatabase database;
 extern LWorldList world_list;
 
 
+bool LoginDatabase::ConnectNewDatabase() {
+	return dbLogin.Connect();
+}
+
+void LoginDatabase::RemoveDeletedCharacterData()
+{
+	dbLogin.Query("DELETE FROM login_char_colors WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)");
+	dbLogin.Query("DELETE FROM login_equipment WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)");
+}
+
 void LoginDatabase::SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet){	
 	if(packet){
 		Query query;
@@ -108,6 +121,65 @@ string LoginDatabase::GetZoneDescription(char* name){
 	return ret;
 }
 
+
+int32 LoginDatabase::GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id)
+{
+	int32 ret;
+	Query query;
+	MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id FROM login_characters WHERE server_id = %u AND char_id = %u AND deleted = 0 LIMIT 0,1", server_id, char_id);
+	MYSQL_ROW row;
+	if((row = mysql_fetch_row(result))) {
+		ret = atoi(row[0]);
+	}
+
+	return ret;
+}
+
+void LoginDatabase::SetServerEquipmentAppearances(int32 server_id, map<int32, LoginEquipmentUpdate> equip_updates)
+{
+	if(equip_updates.size() > 0)
+	{
+
+		LogWrite(LOGIN__DEBUG, 0, "Login", "Saving appearance info from world %u...", server_id);
+
+		map<int32, LoginEquipmentUpdate>::iterator equip_itr;
+		stringstream ss;
+		ss << "replace into login_equipment (login_characters_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue, slot) values";
+		int count=0;
+		int32 char_id = 0;
+
+		for(equip_itr = equip_updates.begin(); equip_itr != equip_updates.end(); equip_itr++)
+		{
+			char_id = GetLoginCharacterIDFromWorldCharID(server_id, (int32)equip_itr->second.world_char_id);
+
+			if( char_id == 0 ) // invalid character/world match
+				continue;
+
+			LogWrite(LOGIN__DEBUG, 5, "Login", "--Processing character %u, slot %i , type %i", char_id, (int32)equip_itr->second.slot,(int32)equip_itr->second.type);
+
+			if(count > 0)
+				ss << ", ";
+
+			ss << "(" << char_id << ", ";
+			ss << (int32)equip_itr->second.equip_type << ", ";
+			ss << (int32)equip_itr->second.red << ", ";
+			ss << (int32)equip_itr->second.green << ", ";
+			ss << (int32)equip_itr->second.blue << ", ";
+			ss << (int32)equip_itr->second.highlight_red << ", ";
+			ss << (int32)equip_itr->second.highlight_green << ", ";
+			ss << (int32)equip_itr->second.highlight_blue << ", ";
+			ss << (int32)equip_itr->second.slot << ")";
+
+			count++;
+		}
+
+		if( !dbLogin.Query(ss.str().c_str()) )
+			LogWrite(LOGIN__ERROR, 0, "Login", "Error saving login_equipment data");
+
+	}
+}
+
+
 void LoginDatabase::SetServerZoneDescriptions(int32 server_id, map<int32, LoginZoneUpdate> zone_descriptions){
 	if(zone_descriptions.size() > 0){
 		map<int32, LoginZoneUpdate>::iterator zone_itr;
@@ -231,8 +303,7 @@ void LoginDatabase::LoadCharacters(LoginAccount* acct, int16 version){
 			uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00};
 			for(size_t y=0;y<sizeof(tmp);y++)
 				player->packet->setDataByName("unknown11", tmp[y], y);
-
-			MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",id);
+			MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",atoi(row[21]));
 			if(result3){
 				for(int i=0;(row3 = mysql_fetch_row(result3)) && i<24; i++){
 					player->packet->setEquipmentByName("equip", atoi(row3[1]), atoi(row3[2]), atoi(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoi(row3[7]), atoi(row3[0]));
@@ -925,4 +996,4 @@ void LoginDatabase::UpdateAccountIPAddress(int32 account_id, int32 address){
 	in.s_addr = address;
 	Query query;
 	query.RunQuery2(Q_UPDATE, "update account set ip_address='%s' where id=%lu", inet_ntoa(in), account_id);
-}
+}

+ 7 - 0
EQ2/source/LoginServer/LoginDatabase.h

@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "../common/database.h"
+#include "../common/DatabaseNew.h"
 #include "../common/types.h"
 #include "../common/MiscFunctions.h"
 #include "../common/servertalk.h"
@@ -78,5 +79,11 @@ public:
 	bool ResetWorldServerStatsConnectedTime( LWorld* world );
 	void RemoveOldWorldServerStats();
 	void ResetWorldStats();
+	//devn00b temp
+	bool ConnectNewDatabase();
+	void SetServerEquipmentAppearances(int32 server_id, map<int32, LoginEquipmentUpdate> equip_updates); // JohnAdams: login appearances
+	int32 GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id); // JohnAdams: login appearances
+	void RemoveDeletedCharacterData();
+	DatabaseNew	dbLogin;
 };
 #endif

+ 2 - 1
EQ2/source/LoginServer/makefile

@@ -6,7 +6,8 @@ SF= ../common/Log.o ../common/timer.o ../common/packet_dump.o ../common/unix.o \
    ../common/EQEMuError.o ../common/misc.o ../common/Crypto.o ../common/RC4.o \
    .obj/debug.o .obj/database.o .obj/EQStream.o ../common/xmlParser.o \
    .obj/EQStreamFactory.o .obj/EQPacket.o ../common/CRC16.o ../common/packet_functions.o \
-   ../common/Condition.o ../common/opcodemgr.o ../common/PacketStruct.o ../common/ConfigReader.o
+   ../common/Condition.o ../common/opcodemgr.o ../common/PacketStruct.o ../common/ConfigReader.o \
+   ../common/DatabaseNew.o ../common/DatabaseResult.o
 
 CC=g++
 LINKER=gcc