Browse Source

LUA Commands Added/Updated for controlling primary commands

Fix #120

Examples:

--AddPrimaryEntityCommand(Spawn,NPC,"",0,"") -- removes all the primary entity commands (including hail)

--AddPrimaryEntityCommand(Spawn,NPC,"hail",10000,"hail") -- adds the hail command back to the npc

--SendUpdateDefaultCommand(NPC,10000,"hail") -- returns the bubble option when you hover over the spawn/npc

--AddPrimaryEntityCommand(Spawn,NPC,"hail2",10000,"hail2","",0,0,1) -- 1 at the end sets this as a default DENY list, if Spawn is supplied and not null/not player then that player gets default allow access.  Any other players must be added (either by calling the AddPrimaryEntityCommand or subsequently SetAccessToEntityCommand)

--SetAccessToEntityCommand(Spawn,NPC,"hail2",0) -- removes access to the just created hail2 for that player

--SetAccessToEntityCommand(Spawn,NPC,"hail2",1) -- adds access to hail2 for that Spawn (player)

--RemovePrimaryEntityCommand(NPC,"hail2") -- removes hail2 command for all players
Image 3 years ago
parent
commit
6c7f11fd70

+ 64 - 3
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -3609,13 +3609,22 @@ int EQ2Emu_lua_AddPrimaryEntityCommand(lua_State* state) {
 	string error_text = lua_interface->GetStringValue(state, 6);
 	int16 cast_time = lua_interface->GetInt16Value(state, 7);
 	int32 spell_visual = lua_interface->GetInt32Value(state, 8);
-	if (spawn && player && player->IsPlayer() && name.length() > 0) {
+	bool denyListDefault = (lua_interface->GetInt8Value(state, 9) == 1);
+	if (spawn) {
 		if (distance == 0)
 			distance = 10.0f;
 		if (command.length() == 0)
 			command = name;
-		spawn->AddPrimaryEntityCommand(name.c_str(), distance, command.c_str(), error_text.c_str(), cast_time, spell_visual);
-		player->GetZone()->SendUpdateDefaultCommand(spawn, command.c_str(), distance);
+		if (command.length() < 1 && name.length() < 1)
+		{
+			// have to run this first to send a 'blank' default command, then remove all commands from the list
+			spawn->GetZone()->SendUpdateDefaultCommand(spawn, command.c_str(), distance);
+			spawn->RemovePrimaryCommands();
+		}
+		else
+		{
+			spawn->AddPrimaryEntityCommand(name.c_str(), distance, command.c_str(), error_text.c_str(), cast_time, spell_visual, denyListDefault, (player && player->IsPlayer()) ? (Player*)player : NULL);
+		}
 	}
 	return 0;
 }
@@ -9385,5 +9394,57 @@ int EQ2Emu_lua_SetSeeHide(lua_State* state) {
 		}
 	}
 
+	return 0;
+}
+
+
+int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state)
+{
+	if (!lua_interface)
+		return 0;
+
+	Spawn* player = lua_interface->GetSpawn(state);
+	Spawn* spawn = lua_interface->GetSpawn(state, 2);
+	string command = lua_interface->GetStringValue(state, 3);
+	bool val = (lua_interface->GetInt8Value(state, 4) == 1);
+
+	if (spawn && player && player->IsPlayer())
+	{
+		EntityCommand* cmd = spawn->FindEntityCommand(string(command), true);
+		bool res = false;
+		if (cmd)
+			res = spawn->SetPermissionToEntityCommand(cmd, (Player*)player, val);
+
+		lua_interface->SetBooleanValue(state, res);
+		return 1;
+	}
+
+	return 0;
+}
+
+int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state)
+{
+	if (!lua_interface)
+		return 0;
+
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	string command = lua_interface->GetStringValue(state, 2);
+
+	if (spawn && command.length() > 0)
+		spawn->RemovePrimaryEntityCommand(command.c_str());
+
+	return 0;
+}
+
+
+int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state) {
+	Spawn* spawn = lua_interface->GetSpawn(state);
+	float distance = lua_interface->GetFloatValue(state, 2);
+	string command = lua_interface->GetStringValue(state, 3);
+	Spawn* player = lua_interface->GetSpawn(state, 4);
+
+	if (spawn) {
+		spawn->GetZone()->SendUpdateDefaultCommand(spawn, command.c_str(), distance, player);
+	}
 	return 0;
 }

+ 4 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -425,4 +425,8 @@ int EQ2Emu_lua_AddSpawnProximity(lua_State* state);
 int EQ2Emu_lua_CanSeeInvis(lua_State* state);
 int EQ2Emu_lua_SetSeeInvis(lua_State* state);
 int EQ2Emu_lua_SetSeeHide(lua_State* state);
+
+int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state);
+int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state);
+int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state);
 #endif

+ 5 - 0
EQ2/source/WorldServer/LuaInterface.cpp

@@ -1028,6 +1028,10 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "CanSeeInvis", EQ2Emu_lua_CanSeeInvis);
 	lua_register(state, "SetSeeInvis", EQ2Emu_lua_SetSeeInvis);
 	lua_register(state, "SetSeeHide", EQ2Emu_lua_SetSeeHide);
+
+	lua_register(state, "SetAccessToEntityCommand", EQ2Emu_lua_SetAccessToEntityCommand);
+	lua_register(state, "RemovePrimaryEntityCommand", EQ2Emu_lua_RemovePrimaryEntityCommand);
+	lua_register(state, "SendUpdateDefaultCommand", EQ2Emu_lua_SendUpdateDefaultCommand);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {
@@ -1591,6 +1595,7 @@ bool LuaInterface::RunItemScript(string script_name, const char* function_name,
 bool LuaInterface::RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn, const char* message) {
 	if(!npc || spawn_scripts_reloading)
 		return false;
+
 	lua_State* state = GetSpawnScript(script_name.c_str(), true, true);
 	if(state){
 		Mutex* mutex = GetSpawnScriptMutex(script_name.c_str());

+ 72 - 5
EQ2/source/WorldServer/Spawn.cpp

@@ -107,9 +107,8 @@ Spawn::Spawn(){
 }
 
 Spawn::~Spawn(){
-	for(int32 i=0;i<primary_command_list.size();i++){
-		safe_delete(primary_command_list[i]);
-	}
+	RemovePrimaryCommands();
+
 	for(int32 i=0;i<secondary_command_list.size();i++){
 		safe_delete(secondary_command_list[i]);
 	}
@@ -143,6 +142,14 @@ Spawn::~Spawn(){
 	RemoveSpawnProximities();
 }
 
+void Spawn::RemovePrimaryCommands()
+{
+	for (int32 i = 0; i < primary_command_list.size(); i++) {
+		safe_delete(primary_command_list[i]);
+	}
+	primary_command_list.clear();
+}
+
 void Spawn::InitializeHeaderPacketData(Player* player, PacketStruct* header, int16 index) {
 	header->setDataByName("index", index);
 
@@ -1537,17 +1544,21 @@ void Spawn::SetPrimaryCommands(vector<EntityCommand*>* commands){
 	}
 }
 
-EntityCommand* Spawn::FindEntityCommand(string command) {
+EntityCommand* Spawn::FindEntityCommand(string command, bool primaryOnly) {
 	EntityCommand* entity_command = 0;
 	if (primary_command_list.size() > 0) {
 		vector<EntityCommand*>::iterator itr;
 		for (itr = primary_command_list.begin(); itr != primary_command_list.end(); itr++) {
-			if ((*itr)->command == command) {
+			if ((*itr)->command.compare(command) == 0) {
 				entity_command = *itr;
 				break;
 			}
 		}
 	}
+
+	if (primaryOnly)
+		return entity_command;
+
 	if (!entity_command && secondary_command_list.size() > 0) {
 		vector<EntityCommand*>::iterator itr;
 		for (itr = secondary_command_list.begin(); itr != secondary_command_list.end(); itr++) {
@@ -3246,4 +3257,60 @@ void Spawn::RemoveSpawnFromProximity(int32 spawnValue, SpawnProximityType type)
 				prox->spawns_in_proximity.erase(spawnValue);
 		}
 	}
+}
+
+void Spawn::AddPrimaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual, bool defaultDenyList, Player* player) {
+
+	EntityCommand* cmd = FindEntityCommand(string(command), true);
+
+	bool newCommand = false;
+	if (!cmd)
+	{
+		newCommand = true;
+		cmd = CreateEntityCommand(name, distance, command, error_text, cast_time, spell_visual, !defaultDenyList);
+	}
+
+	if (defaultDenyList)
+		SetPermissionToEntityCommand(cmd, player, true);
+
+	if (newCommand)
+		primary_command_list.push_back(cmd);
+}
+
+void Spawn::RemovePrimaryEntityCommand(const char* command) {
+	vector<EntityCommand*>::iterator itr;
+	string tmpStr(command);
+	for (itr = primary_command_list.begin(); itr != primary_command_list.end();) {
+		EntityCommand* cmd = *itr;
+		if (cmd->command.compare(tmpStr) == 0)
+		{
+			safe_delete(cmd);
+
+			vector<EntityCommand*>::iterator tmpItr = itr++;
+			primary_command_list.erase(itr);
+
+			if (tmpItr == primary_command_list.end())
+				break;
+
+			itr = tmpItr;
+		}
+		else
+			itr++;
+	}
+}
+
+bool Spawn::SetPermissionToEntityCommand(EntityCommand* command, Player* player, bool permissionValue)
+{
+	if (player != NULL)
+	{
+		map<int32, bool>::iterator itr = command->allow_or_deny.find(player->GetCharacterID());
+		if (itr == command->allow_or_deny.end())
+			command->allow_or_deny.insert(make_pair(player->GetCharacterID(), permissionValue));
+		else if (itr->second != permissionValue)
+			itr->second = permissionValue;
+
+		return true;
+	}
+
+	return false;
 }

+ 10 - 5
EQ2/source/WorldServer/Spawn.h

@@ -279,9 +279,10 @@ public:
 		entity_command->error_text = old_command->error_text;
 		entity_command->cast_time = old_command->cast_time;
 		entity_command->spell_visual = old_command->spell_visual;
+		entity_command->default_allow_list = old_command->default_allow_list;
 		return entity_command;
 	}
-	EntityCommand* CreateEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual){
+	EntityCommand* CreateEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual, bool default_allow_list=true){
 		EntityCommand* entity_command = new EntityCommand;
 		entity_command->name = name;
 		entity_command->distance = distance;
@@ -289,12 +290,14 @@ public:
 		entity_command->error_text = error_text;
 		entity_command->cast_time = cast_time;
 		entity_command->spell_visual = spell_visual;
+		entity_command->default_allow_list = default_allow_list;
 		return entity_command;
 	}
 	void AddChangedZoneSpawn();
-	void AddPrimaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual){
-		primary_command_list.push_back(CreateEntityCommand(name, distance, command, error_text, cast_time, spell_visual));
-	}
+	void AddPrimaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual, bool defaultDenyList = false, Player* player = NULL);
+	void RemovePrimaryEntityCommand(const char* command);
+	bool SetPermissionToEntityCommand(EntityCommand* command, Player* player, bool permissionValue);
+
 	void AddSecondaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual){
 		secondary_command_list.push_back(CreateEntityCommand(name, distance, command, error_text, cast_time, spell_visual));
 	}
@@ -760,7 +763,7 @@ public:
 	void SetSecondaryCommands(vector<EntityCommand*>* commands);
 	vector<EntityCommand*>* GetPrimaryCommands() {return &primary_command_list;}
 	vector<EntityCommand*>* GetSecondaryCommands() {return &secondary_command_list;}
-	EntityCommand* FindEntityCommand(string command);
+	EntityCommand* FindEntityCommand(string command, bool primaryOnly=false);
 	virtual EQ2Packet* serialize(Player* player, int16 version);
 	EQ2Packet* spawn_serialize(Player* player, int16 version);
 	EQ2Packet* spawn_update_packet(Player* player, int16 version, bool override_changes = false, bool override_vis_changes = false);
@@ -879,6 +882,8 @@ public:
 	int32	GetTransporterID();
 	bool	MeetsSpawnAccessRequirements(Player* player);
 
+	void	RemovePrimaryCommands();
+
 	void	InitializePosPacketData(Player* player, PacketStruct* packet, bool bSpawnUpdate = false);
 	void	InitializeInfoPacketData(Player* player, PacketStruct* packet);
 	void	InitializeVisPacketData(Player* player, PacketStruct* packet);

+ 3 - 0
EQ2/source/WorldServer/SpawnLists.h

@@ -21,6 +21,7 @@
 #define EQ2_SPAWN_LISTS
 #include "../common/types.h"
 #include <vector>
+#include <map>
 
 #define SPAWN_ENTRY_TYPE_NPC		0
 #define SPAWN_ENTRY_TYPE_OBJECT		1
@@ -35,6 +36,8 @@ struct EntityCommand{
 	string	command;
 	int16	cast_time;
 	int32	spell_visual;
+	map<int32, bool> allow_or_deny; // this is a map of player IDs and whether they are allowed on the command or denied
+	bool default_allow_list; // if set to false then its a defaultDenyList
 };
 struct SpawnEntry{
 	int32	spawn_entry_id;

+ 1 - 0
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -3662,6 +3662,7 @@ void WorldDatabase::LoadEntityCommands(ZoneServer* zone) {
 		command->error_text = result.GetString(4);
 		command->cast_time = result.GetInt16(5);
 		command->spell_visual = result.GetInt32(6);
+		command->default_allow_list = true;
 
 		zone->SetEntityCommandList(id, command);
 		LogWrite(COMMAND__DEBUG, 5, "Command", "---Loading Command: '%s' (%s)", command->name.c_str(), command->command.c_str());

+ 30 - 0
EQ2/source/WorldServer/client.cpp

@@ -3432,7 +3432,23 @@ void Client::HandleVerbRequest(EQApplicationPacket* app) {
 		vector<EntityCommand*> delete_commands;
 		if (out && spawn) {
 			for (int32 i = 0; i < spawn->primary_command_list.size(); i++)
+			{
+				// default is a deny list not allow, only allow if on the iterator list and itr second is not false (deny)
+				if (!spawn->primary_command_list[i]->default_allow_list)
+				{
+					map<int32, bool>::iterator itr = spawn->primary_command_list[i]->allow_or_deny.find(GetPlayer()->GetCharacterID());
+					if (itr == spawn->primary_command_list[i]->allow_or_deny.end() || !itr->second)
+						continue;
+				}
+				else
+				{
+					// default is allow list, only deny if added to the list as deny (false itr second)
+					map<int32, bool>::iterator itr = spawn->primary_command_list[i]->allow_or_deny.find(GetPlayer()->GetCharacterID());
+					if (itr != spawn->primary_command_list[i]->allow_or_deny.end() && !itr->second)
+						continue;
+				}
 				commands.push_back(spawn->primary_command_list[i]);
+			}
 			for (int32 i = 0; i < spawn->secondary_command_list.size(); i++)
 				commands.push_back(spawn->secondary_command_list[i]);
 			if (spawn->IsPlayer()) {
@@ -8470,4 +8486,18 @@ void Client::SendHailCommand(Spawn* spawn)
 			break;
 		}
 	}
+}
+
+void Client::SendDefaultCommand(Spawn* spawn, const char* command, float distance)
+{
+	if (GetPlayer()->WasSentSpawn(spawn->GetID()) && GetPlayer()->WasSpawnRemoved(spawn) == false) {
+		PacketStruct* packet = configReader.getStruct("WS_SetDefaultCommand", GetVersion());
+		if (packet) {
+			packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
+			packet->setMediumStringByName("command_name", command);
+			packet->setDataByName("distance", distance);
+			QueuePacket(packet->serialize());
+			safe_delete(packet);
+		}
+	}
 }

+ 1 - 0
EQ2/source/WorldServer/client.h

@@ -403,6 +403,7 @@ public:
 	bool IsZonedIn() { return connected_to_zone; }
 
 	void SendHailCommand(Spawn* target);
+	void SendDefaultCommand(Spawn* spawn, const char* command, float distance);
 private:
 	void    SavePlayerImages();
 	void	SkillChanged(Skill* skill, int16 previous_value, int16 new_value);

+ 22 - 12
EQ2/source/WorldServer/zoneserver.cpp

@@ -5172,7 +5172,24 @@ EQ2Packet* ZoneServer::GetZoneInfoPacket(Client* client){
 	return outapp;
 }
 
-void ZoneServer::SendUpdateDefaultCommand(Spawn* spawn, const char* command, float distance){
+void ZoneServer::SendUpdateDefaultCommand(Spawn* spawn, const char* command, float distance, Spawn* toPlayer){
+	if (spawn == nullptr || command == nullptr)
+		return;
+
+	if (toPlayer)
+	{
+		if (!toPlayer->IsPlayer())
+			return;
+
+		Client* client = GetClientBySpawn(toPlayer);
+		if (client)
+		{
+			client->SendDefaultCommand(spawn, command, distance);
+		}
+		// we don't override the primary command cause that would change ALL clients
+		return;
+	}
+
 	Client* client = 0;
 	PacketStruct* packet = 0;
 	vector<Client*>::iterator client_itr;
@@ -5180,18 +5197,11 @@ void ZoneServer::SendUpdateDefaultCommand(Spawn* spawn, const char* command, flo
 	MClientList.readlock(__FUNCTION__, __LINE__);
 	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
 		client = *client_itr;
-		if(client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && client->GetPlayer()->WasSpawnRemoved(spawn) == false){
-			packet = configReader.getStruct("WS_SetDefaultCommand", client->GetVersion());
-			if(packet){
-				packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
-				packet->setMediumStringByName("command_name", command);
-				packet->setDataByName("distance", distance);
-				client->QueuePacket(packet->serialize());
-				safe_delete(packet);
-			}
-		}
-		spawn->SetPrimaryCommand(command, command, distance);
+		client->SendDefaultCommand(spawn, command, distance);
 	}
+	
+	if (strlen(command)>0)
+		spawn->SetPrimaryCommand(command, command, distance);
 	MClientList.releasereadlock(__FUNCTION__, __LINE__);
 }
 

+ 1 - 1
EQ2/source/WorldServer/zoneserver.h

@@ -404,7 +404,7 @@ public:
     void SendDispellPacket(Entity* caster, Spawn* target, string dispell_name, string spell_name, int8 dispell_type);
 
 	void SetupInstance(int32 createdInstanceID=0);
-	void SendUpdateDefaultCommand(Spawn* spawn, const char* command, float distance);
+	void SendUpdateDefaultCommand(Spawn* spawn, const char* command, float distance, Spawn* toplayer = NULL);
 
 	map<int32, int32>* GetSpawnLocationsByGroup(int32 group_id);