/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* GameMonkey Script License
* Copyright (c) 2003 Auran Development Ltd.
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
*/
#include "StdAfx.h"
ScriptEngine * ScriptSystem;
#ifndef WIN32
#include
#endif
ScriptEngine::ScriptEngine()
{
m_playerType = m_unitType = m_gameObjectType = m_questType = m_spellType = m_auraType = m_areaTriggerType = m_scriptEngineType = -1;
m_machine = 0;
}
ScriptEngine::~ScriptEngine()
{
if(m_machine)
{
//for(uint32 i = 0; i < 10; ++i)
//m_machine->DestructDeleteObject(m_userObjects[i]);
m_machine->GetGC()->DestructAll();
delete m_machine;
}
}
void ScriptEngine::Reload()
{
if(m_machine)
{
m_playerType = m_unitType = m_gameObjectType = m_questType = m_spellType = m_auraType = m_areaTriggerType = m_scriptEngineType = -1;
for(int i = 0; i < 10; ++i)
m_userObjects[i] = 0;
m_PlayerStorageMap.clear();
m_unitMap.clear();
m_gameObjectMap.clear();
m_questMap.clear();
m_auraMap.clear();
m_areaTriggerMap.clear();
m_machine->ResetAndFreeMemory();
delete m_machine;
m_machine = 0;
}
m_machine = new gmMachine;
/* create our types */
m_playerType = m_machine->CreateUserType("Player");
m_unitType = m_machine->CreateUserType("Unit");
m_gameObjectType = m_machine->CreateUserType("GameObject");
m_questType = m_machine->CreateUserType("Quest");
m_spellType = m_machine->CreateUserType("Spell");
m_auraType = m_machine->CreateUserType("Aura");
m_areaTriggerType = m_machine->CreateUserType("AreaTrigger");
m_scriptEngineType = m_machine->CreateUserType("ScriptEngine");
m_allowedTypes.push_back(m_playerType);
m_allowedTypes.push_back(m_unitType);
m_allowedTypes.push_back(m_gameObjectType);
m_allowedTypes.push_back(m_questType);
m_allowedTypes.push_back(m_spellType);
m_allowedTypes.push_back(m_auraType);
m_allowedTypes.push_back(m_areaTriggerType);
m_allowedTypes.push_back(m_scriptEngineType);
/* register all our functions inside the machine */
SetPlayerFunctionTable();
SetUnitFunctionTable();
SetGameObjectFunctionTable();
SetQuestFunctionTable();
SetSpellFunctionTable();
SetAuraFunctionTable();
SetAreaTriggerFunctionTable();
SetScriptEngineFunctionTable();
/* allocate our user data variables */
for(int i = 0; i < 10; ++i)
{
m_userObjects[i] = m_machine->AllocUserObject(this, m_scriptEngineType);
m_machine->AddCPPOwnedGMObject(m_userObjects[i]);
}
sLog.outString("Compiling GameMonkey Scripts...");
#ifdef WIN32
/* compile the scripts */
WIN32_FIND_DATA fd;
HANDLE f = FindFirstFile("scripts\\*.gm", &fd);
if(f != INVALID_HANDLE_VALUE)
{
do
{
string fname = "scripts/";
fname += fd.cFileName;
ExecuteScriptFile(fname.c_str());
} while(FindNextFile(f, &fd));
FindClose(f);
}
#else
/* compile scripts */
struct dirent ** list;
int filecount = scandir("scripts/", &list, 0, 0);
if(!filecount || !list || filecount < 0)
return;
char * ext;
while(filecount--)
{
ext = strrchr(list[filecount]->d_name, '.');
if(ext != NULL && !strcmp(ext, ".gm"))
{
string full_path = "scripts/" + string(list[filecount]->d_name);
ExecuteScriptFile(full_path.c_str());
}
free(list[filecount]);
}
free(list);
#endif
printf("\nScripts compiled.\n\n");
}
void ScriptEngine::ExecuteScriptFile(const char * filename)
{
char * data;
FILE * f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
int size = ftell(f);
fseek(f, 0, SEEK_SET);
data = new char[size+1];
fread(data, 1, size, f);
data[size] = 0;
fclose(f);
/* setup 'this' pointer */
m_userObjects[0]->m_user = this;
m_userObjects[0]->m_userType = m_scriptEngineType;
/* set 'this' variable */
m_variables[0].SetUser(m_userObjects[0]);
int threadid;
printf(" %s: ", strstr(filename, "/")+1);
int no_errors = m_machine->ExecuteString(data, &threadid, true, filename, &m_variables[0]);
printf("%u errors.\n", no_errors);
if(no_errors)
{
printf("Errors occured while compiling %s.\n", filename);
DumpErrors();
}
delete [] data;
}
void ScriptEngine::DumpErrors()
{
// sLog.outString("Dumping errors from script action: ");
bool first = true;
const char * message = m_machine->GetLog().GetEntry(first);
while(message)
{
printf("GM_Debug: %s", message);
first = false;
message = m_machine->GetLog().GetEntry(first);
}
// sLog.outString("End of error dump.");
}
void ScriptEngine::DoGMCall(gmFunctionObject * obj, uint32 ArgumentCount, int * return_value)
{
gmCall call;
if(call.BeginFunction(m_machine, obj, m_variables[0], false))
{
for(uint32 i = 0; i < ArgumentCount; ++i)
call.AddParam(m_variables[1+i]);
m_userObjectCounter = ArgumentCount + 1;
call.End();
if(return_value != 0)
{
int v;
if(call.GetReturnedInt(v))
*return_value = v;
else
*return_value = 1;
}
DumpErrors();
}
else
{
printf("Could not find function!");
DumpErrors();
}
}
bool ScriptEngine::OnActivateAreaTrigger(AreaTrigger * at, Player * plr)
{
SingleScriptMap::iterator itr = m_areaTriggerMap.find(at->AreaTriggerID);
if(itr == m_areaTriggerMap.end())
return true;
/*map::iterator it2 = itr->second.find(0);
if(it2 == itr->second.end())
return true;*/
m_lock.Acquire();
ASSERT(itr->second->GetType() == GM_FUNCTION);
// Setup 'this' pointer to the areatrigger struct.
m_userObjects[0]->m_user = at;
m_userObjects[0]->m_userType = m_areaTriggerType;
m_variables[0].SetUser(m_userObjects[0]);
// Setup the first argument (the player entering)
m_userObjects[1]->m_user = plr;
m_userObjects[1]->m_userType = m_playerType;
m_variables[1].SetUser(m_userObjects[1]);
m_userObjectCounter = 2;
// Setup the call.
gmCall call;
if(call.BeginFunction(m_machine, itr->second, m_variables[0], false))
{
call.AddParam(m_variables[1]);
call.End();
DumpErrors();
int res;
if(!call.GetReturnedInt(res))
{
printf("Call failed.");
m_lock.Release();
return true;
}
m_lock.Release();
return (res > 0) ? true : false;
}
else
{
printf("Could not find function!");
DumpErrors();
m_lock.Release();
return true;
}
}
bool ScriptEngine::OnQuestEvent(Quest * quest, Creature * pQuestGiver, Player * plr, uint32 Event)
{
if(!m_questMap.size())
return false;
ScriptMap::iterator itr = m_questMap.find(quest->id);
if(itr == m_questMap.end())
return false;
map::iterator it2 = itr->second.find(Event);
if(it2 == itr->second.end() )
return false;
m_lock.Acquire();
SetVariable(0, quest, m_questType);
SetVariable(1, pQuestGiver, m_unitType);
SetVariable(2, plr, m_playerType);
DoGMCall(it2->second, 2, 0);
m_lock.Release();
return true;
}
bool ScriptEngine::OnQuestRequireEvent(Quest * quest, Creature * pQuestGiver, Player * plr, uint32 Event)
{
if(!m_questMap.size())
return true;;
ScriptMap::iterator itr = m_questMap.find(quest->id);
if(itr == m_questMap.end())
return true;
map::iterator it2 = itr->second.find(Event);
if(it2 == itr->second.end() )
return true;
m_lock.Acquire();
SetVariable(0, quest, m_questType);
SetVariable(1, pQuestGiver, m_unitType);
SetVariable(2, plr, m_playerType);
int ret;
DoGMCall(it2->second, 2, &ret);
m_lock.Release();
return (ret > 0) ? true : false;
}
bool ScriptEngine::OnCreatureEvent(Creature * pCreature, Unit * pAttacker, uint32 Event)
{
if(!m_unitMap.size())
return false;
ScriptMap::iterator itr = m_unitMap.find(pCreature->GetEntry());
if(itr == m_unitMap.end())
return false;
map::iterator it2 = itr->second.find(Event);
if(it2 == itr->second.end() )
return false;
m_lock.Acquire();
SetVariable(0, pCreature, m_unitType);
SetVariable(1, pAttacker, m_playerType);
DoGMCall(it2->second, 1, 0);
m_lock.Release();
return true;
}
bool ScriptEngine::OnCreatureEventArg(Creature * pCreature, uint32 Argument, uint32 Event)
{
if(!m_unitMap.size())
return false;
ScriptMap::iterator itr = m_unitMap.find(pCreature->GetEntry());
if(itr == m_unitMap.end())
return false;
map::iterator it2 = itr->second.find(Event);
if(it2 == itr->second.end() )
return false;
gmFunctionObject * obj = it2->second;
m_lock.Acquire();
ASSERT(obj->GetType() == GM_FUNCTION);
SetVariable(0, pCreature, m_unitType);
gmCall call;
if(call.BeginFunction(m_machine, obj, m_variables[0], false))
{
call.AddParamInt(Argument);
m_userObjectCounter = 2;
call.End();
DumpErrors();
}
else
{
printf("Could not find function!");
DumpErrors();
}
m_lock.Release();
return true;
}
bool ScriptEngine::OnCreatureEvent(Creature * pCreature, gmFunctionObject * pointer)
{
if(!m_unitMap.size())
return false;
m_lock.Acquire();
SetVariable(0, pCreature, m_unitType);
DoGMCall(pointer, 0, 0);
m_lock.Release();
return true;
}
bool ScriptEngine::OnGameObjectEvent(GameObject * pGameObject, Player * pUser, uint32 Event)
{
if(!m_gameObjectMap.size())
return false;
ScriptMap::iterator itr = m_gameObjectMap.find(pGameObject->GetEntry());
if(itr == m_gameObjectMap.end())
return false;
map::iterator it2 = itr->second.find(Event);
if(it2 == itr->second.end() )
return false;
m_lock.Acquire();
SetVariable(0, pGameObject, m_gameObjectType);
SetVariable(1, pUser, m_playerType);
DoGMCall(it2->second, 1, 0);
m_lock.Release();
return true;
}
bool ScriptEngine::HasEventType(uint32 Entry, uint32 Event)
{
ScriptMap::iterator itr = m_unitMap.find(Entry);
if(itr == m_unitMap.end())
return false;
if(itr->second.find(Event) != itr->second.end())
return false;
return true;
}