mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
Fixed an error on shutdown when running UOX3 in debug mode in VS due to CCommands constructor attempting to unnecessarily clear CommandMap, TargetMap and JSCommandMaps Fixed an issue in Portuguese dictionary where a commented line was lacking a forward slash Fixed some soft exceptions related to loading of dictionary files Fixed soft exceptions related to checking file sizes of potentially non-existing worldfiles on startup Fixed some soft exceptions related to loading of regions from DFNs and townregions from WSC Fixed misordered datatype entries in dfnDataTypes for DFNTAG_DYEHAIR and DFNTAG_DEFENSECHANCE Fixed double "GET" entry in strToDFNTag mapping. One of them should've been "GETUO" Fixed strToDFNTag mapping error for "LOYALTY" DFN tag, which was being incorrectly mapped to DFNTAG_MAXLOYALTY Added missing strToDFNTag entry for WILLTHIRST DFN tag Removed unknown and unused DFN tag/mapping named SKILLVAR Fixed misordered DFNTAG_SWINGSPEEDINCREASE entry in DFNTAGS enum, which caused tags following it to be associated with incorrect datatypes Fixed some crash/slowdown issues with reloading of all JS scripts (console menu option 8), especially noticable in VS debug mode Fixed an issue where shopkeepers would sometimes fail to recognize items they should want to buy from players due to section ID case sensitivity
429 lines
15 KiB
C++
429 lines
15 KiB
C++
//o------------------------------------------------------------------------------------------------o
|
|
//| File - CJSEngine.cpp
|
|
//| Date - 2/22/2006
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Purpose - JS Engine Handling
|
|
//o------------------------------------------------------------------------------------------------o
|
|
//| Changes - Version History
|
|
//|
|
|
//| 1.0 2/22/2006
|
|
//| Extracted JSObject handling from cScript and encapsulated it in a global class.
|
|
//| Moved global JSEngine loading code into the class.
|
|
//| Created CJSRuntime class to handle multiple runtimes (for threading purposes).
|
|
//o------------------------------------------------------------------------------------------------o
|
|
#include "uox3.h"
|
|
#include "CJSEngine.h"
|
|
#include "UOXJSClasses.h"
|
|
#include "UOXJSMethods.h"
|
|
#include "UOXJSPropertySpecs.h"
|
|
#include <algorithm>
|
|
|
|
CJSEngine *JSEngine = nullptr;
|
|
|
|
//==================================================================================================
|
|
auto CJSEngine::Startup() -> void
|
|
{
|
|
runtimeList.resize( 0 );
|
|
const UI32 maxEngineSize = 0xFFFFFFFF; // 4 gb, hard max
|
|
|
|
// 16 MB minimum. Any lower and UOX3 is prone to crashes from frequent JS reloads
|
|
auto maxBytesSize = std::max( static_cast<UI16>( 16 ), cwmWorldState->ServerData()->GetJSEngineSize() ); // from INI
|
|
|
|
// Use minimum of INI-provided value and hard-defined maximum
|
|
// maxBytes definition: "Maximum nominal heap before last ditch GC"
|
|
UI32 engineMaxBytes = std::min( static_cast<UI32>( static_cast<UI32>( maxBytesSize ) * 1024 * 1024 ), maxEngineSize );
|
|
|
|
Console.PrintSectionBegin();
|
|
Console << "Starting JavaScript Engine...." << myendl;
|
|
|
|
runtimeList.push_back( new CJSRuntime( engineMaxBytes )); // Default Runtime
|
|
runtimeList.push_back( new CJSRuntime( engineMaxBytes )); // Console Runtime
|
|
|
|
Console << "JavaScript engine startup complete." << myendl;
|
|
Console.PrintSectionBegin();
|
|
}
|
|
//===================================================================
|
|
CJSEngine::~CJSEngine()
|
|
{
|
|
// Why? we are shutting down, the process memory will take care of this for us in theory
|
|
/*
|
|
for( RUNTIMELIST_ITERATOR rIter = runtimeList.begin(); rIter != runtimeList.end(); ++rIter )
|
|
{
|
|
if(( *rIter))
|
|
{
|
|
delete ( *rIter );
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
void CJSEngine::Reload( void )
|
|
{
|
|
for( RUNTIMELIST_ITERATOR rIter = runtimeList.begin(); rIter != runtimeList.end(); ++rIter )
|
|
{
|
|
if(( *rIter ) != nullptr )
|
|
{
|
|
( *rIter )->Reload();
|
|
}
|
|
}
|
|
}
|
|
void CJSEngine::CollectGarbage( void )
|
|
{
|
|
for( RUNTIMELIST_ITERATOR rIter = runtimeList.begin(); rIter != runtimeList.end(); ++rIter )
|
|
{
|
|
if(( *rIter ) != nullptr )
|
|
{
|
|
( *rIter )->CollectGarbage();
|
|
}
|
|
}
|
|
}
|
|
|
|
JSRuntime *CJSEngine::GetRuntime( UI08 runTime ) const
|
|
{
|
|
if( runTime >= runtimeList.size() )
|
|
{
|
|
runTime = 0;
|
|
}
|
|
|
|
return runtimeList[runTime]->GetRuntime();
|
|
}
|
|
JSContext *CJSEngine::GetContext( UI08 runTime ) const
|
|
{
|
|
if( runTime >= runtimeList.size() )
|
|
{
|
|
runTime = 0;
|
|
}
|
|
|
|
return runtimeList[runTime]->GetContext();
|
|
}
|
|
JSObject *CJSEngine::GetObject( UI08 runTime ) const
|
|
{
|
|
if( runTime >= runtimeList.size() )
|
|
{
|
|
runTime = 0;
|
|
}
|
|
|
|
return runtimeList[runTime]->GetObject();
|
|
}
|
|
|
|
UI08 CJSEngine::FindActiveRuntime( JSRuntime *rT ) const
|
|
{
|
|
for( RUNTIMELIST_CITERATOR rIter = runtimeList.begin(); rIter != runtimeList.end(); ++rIter )
|
|
{
|
|
if(( *rIter ) != nullptr )
|
|
{
|
|
if( rT == ( *rIter )->GetRuntime() )
|
|
return static_cast<UI08>( rIter - runtimeList.begin() );
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
JSObject *CJSEngine::GetPrototype( UI08 runTime, JSPrototypes protoNum ) const
|
|
{
|
|
JSObject *retVal = nullptr;
|
|
if( runTime < runtimeList.size() )
|
|
{
|
|
retVal = runtimeList[runTime]->GetPrototype( protoNum );
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
JSObject *CJSEngine::AcquireObject( IUEEntries iType, void *index, UI08 runTime )
|
|
{
|
|
JSObject *retVal = nullptr;
|
|
if( index != nullptr && runTime < runtimeList.size() )
|
|
{
|
|
retVal = runtimeList[runTime]->AcquireObject( iType, index );
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
void CJSEngine::ReleaseObject( IUEEntries iType, void *index )
|
|
{
|
|
for( RUNTIMELIST_ITERATOR rIter = runtimeList.begin(); rIter != runtimeList.end(); ++rIter )
|
|
{
|
|
if(( *rIter ) != nullptr )
|
|
{
|
|
( *rIter )->ReleaseObject( iType, index );
|
|
}
|
|
}
|
|
}
|
|
|
|
//========================================================================================================
|
|
// CJSRuntime
|
|
//======================================================================================================
|
|
CJSRuntime::CJSRuntime( UI32 engineSize )
|
|
{
|
|
jsRuntime = JS_NewRuntime( engineSize );
|
|
if( jsRuntime == nullptr )
|
|
{
|
|
Shutdown( FATAL_UOX3_JAVASCRIPT );
|
|
}
|
|
|
|
// No need to use a large number here, it's not stack size as documentation indicated,
|
|
// but "chunk size of the stack pool". Recommendations are to leave it at 8192. In
|
|
// debug builds, larger values can even degrade performance drastically
|
|
jsContext = JS_NewContext( jsRuntime, 8192 );
|
|
|
|
// Specify JS 1.8 as version to unlock const variables, let, etc
|
|
JS_SetVersion( jsContext, JSVERSION_1_8 );
|
|
|
|
if( jsContext == nullptr )
|
|
{
|
|
Shutdown( FATAL_UOX3_JAVASCRIPT );
|
|
}
|
|
|
|
jsGlobal = JS_NewObject( jsContext, &global_class, nullptr, nullptr );
|
|
if( jsGlobal == nullptr )
|
|
{
|
|
Shutdown( FATAL_UOX3_JAVASCRIPT );
|
|
}
|
|
JS_LockGCThing( jsContext, jsGlobal );
|
|
JS_InitStandardClasses( jsContext, jsGlobal );
|
|
|
|
objectList.resize( IUE_COUNT );
|
|
|
|
InitializePrototypes();
|
|
}
|
|
CJSRuntime::~CJSRuntime( void )
|
|
{
|
|
Cleanup();
|
|
|
|
JS_UnlockGCThing( jsContext, spellsObj );
|
|
JS_UnlockGCThing( jsContext, skillsObj );
|
|
JS_UnlockGCThing( jsContext, accountsObj );
|
|
JS_UnlockGCThing( jsContext, consoleObj );
|
|
JS_UnlockGCThing( jsContext, createEntriesObj );
|
|
JS_UnlockGCThing( jsContext, timerObj );
|
|
JS_UnlockGCThing( jsContext, scriptObj );
|
|
|
|
for( size_t i = JSP_ITEM; i < JSP_COUNT; ++i )
|
|
{
|
|
JS_UnlockGCThing( jsContext, protoList[i] );
|
|
}
|
|
JS_UnlockGCThing( jsContext, jsGlobal );
|
|
JS_DestroyContext( jsContext );
|
|
JS_DestroyRuntime( jsRuntime );
|
|
}
|
|
|
|
void CJSRuntime::Cleanup( void )
|
|
{
|
|
std::vector<JSOBJECTMAP>::iterator oIter;
|
|
for( oIter = objectList.begin(); oIter != objectList.end(); ++oIter )
|
|
{
|
|
JSOBJECTMAP& ourList = ( *oIter );
|
|
for( JSOBJECTMAP_ITERATOR lIter = ourList.begin(); lIter != ourList.end(); ++lIter )
|
|
{
|
|
JS_UnlockGCThing( jsContext, ( *lIter ).second );
|
|
JS_SetPrivate( jsContext, ( *lIter ).second, nullptr );
|
|
}
|
|
ourList.clear();
|
|
}
|
|
objectList.resize( 0 );
|
|
}
|
|
void CJSRuntime::Reload()
|
|
{
|
|
Cleanup();
|
|
|
|
objectList.resize( IUE_COUNT );
|
|
}
|
|
void CJSRuntime::CollectGarbage()
|
|
{
|
|
JS_GC( jsContext );
|
|
}
|
|
|
|
void CJSRuntime::InitializePrototypes()
|
|
{
|
|
protoList.resize( JSP_COUNT );
|
|
|
|
JSContext *cx = jsContext;
|
|
JSObject *obj = jsGlobal;
|
|
|
|
protoList[JSP_CHAR] = JS_InitClass( cx, obj, nullptr, &UOXChar_class.base, nullptr, 0, CCharacterProps, CChar_Methods, nullptr, nullptr );
|
|
protoList[JSP_ITEM] = JS_InitClass( cx, obj, nullptr, &UOXItem_class.base, nullptr, 0, CItemProps, CItem_Methods, nullptr, nullptr );
|
|
protoList[JSP_SPELL] = JS_InitClass( cx, obj, nullptr, &UOXSpell_class, nullptr, 0, CSpellProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_SPELLS] = JS_InitClass( cx, obj, nullptr, &UOXSpells_class, nullptr, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_GLOBALSKILL] = JS_InitClass( cx, obj, nullptr, &UOXGlobalSkill_class, nullptr, 0, CGlobalSkillProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_GLOBALSKILLS] = JS_InitClass( cx, obj, nullptr, &UOXGlobalSkills_class, nullptr, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_CREATEENTRY] = JS_InitClass( cx, obj, nullptr, &UOXCreateEntry_class, nullptr, 0, CCreateEntryProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_CREATEENTRIES] = JS_InitClass( cx, obj, nullptr, &UOXCreateEntries_class, nullptr, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_TIMER] = JS_InitClass( cx, obj, nullptr, &UOXTimer_class, nullptr, 0, CTimerProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_SOCK] = JS_InitClass( cx, obj, nullptr, &UOXSocket_class.base, nullptr, 0, CSocketProps, CSocket_Methods, nullptr, nullptr );
|
|
protoList[JSP_ACCOUNTS] = JS_InitClass( cx, obj, nullptr, &UOXAccount_class, nullptr, 0, CAccountProperties, CAccount_Methods, nullptr, nullptr );
|
|
protoList[JSP_CONSOLE] = JS_InitClass( cx, obj, nullptr, &UOXConsole_class, nullptr, 0, CConsoleProperties, CConsole_Methods, nullptr, nullptr );
|
|
protoList[JSP_REGION] = JS_InitClass( cx, obj, nullptr, &UOXRegion_class, nullptr, 0, CRegionProperties, CRegion_Methods, nullptr, nullptr );
|
|
protoList[JSP_SPAWNREGION]= JS_InitClass( cx, obj, nullptr, &UOXSpawnRegion_class, nullptr, 0, CSpawnRegionProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_RESOURCE] = JS_InitClass( cx, obj, nullptr, &UOXResource_class, nullptr, 0, CResourceProperties, nullptr, nullptr, nullptr );
|
|
protoList[JSP_RACE] = JS_InitClass( cx, obj, nullptr, &UOXRace_class, nullptr, 0, CRaceProperties, CRace_Methods, nullptr, nullptr );
|
|
protoList[JSP_GUILD] = JS_InitClass( cx, obj, nullptr, &UOXGuild_class, nullptr, 0, CGuildProperties, CGuild_Methods, nullptr, nullptr );
|
|
protoList[JSP_PARTY] = JS_InitClass( cx, obj, nullptr, &UOXParty_class.base, nullptr, 0, CPartyProperties, CParty_Methods, nullptr, nullptr );
|
|
protoList[JSP_PACKET] = JS_InitClass( cx, obj, nullptr, &UOXPacket_class, Packet, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_GUMP] = JS_InitClass( cx, obj, nullptr, &UOXGump_class, Gump, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_FILE] = JS_InitClass( cx, obj, nullptr, &UOXFile_class, UOXCFile, 0, nullptr, nullptr, nullptr, nullptr );
|
|
protoList[JSP_SCRIPT] = JS_InitClass( cx, obj, nullptr, &uox_class, nullptr, 0, CScriptProperties, nullptr, nullptr, nullptr );
|
|
spellsObj = JS_DefineObject( cx, obj, "Spells", &UOXSpells_class, protoList[JSP_SPELLS], 0 );
|
|
skillsObj = JS_DefineObject( cx, obj, "Skills", &UOXGlobalSkills_class, protoList[JSP_GLOBALSKILLS], 0 );
|
|
accountsObj = JS_DefineObject( cx, obj, "Accounts", &UOXAccount_class, protoList[JSP_ACCOUNTS], 0 );
|
|
consoleObj = JS_DefineObject( cx, obj, "Console", &UOXConsole_class, protoList[JSP_CONSOLE], 0 );
|
|
createEntriesObj = JS_DefineObject( cx, obj, "CreateEntries", &UOXCreateEntries_class, protoList[JSP_CREATEENTRIES], 0 );
|
|
timerObj = JS_DefineObject( cx, obj, "Timer", &UOXTimer_class, protoList[JSP_TIMER], 0 );
|
|
scriptObj = JS_DefineObject( cx, obj, "SCRIPT", &uox_class, protoList[JSP_SCRIPT], 0 );
|
|
JS_LockGCThing( cx, spellsObj );
|
|
JS_LockGCThing( cx, skillsObj );
|
|
JS_LockGCThing( cx, accountsObj );
|
|
JS_LockGCThing( cx, consoleObj );
|
|
JS_LockGCThing( cx, createEntriesObj );
|
|
JS_LockGCThing( cx, timerObj );
|
|
JS_LockGCThing( cx, scriptObj );
|
|
|
|
for( size_t i = JSP_ITEM; i < JSP_COUNT; ++i )
|
|
{
|
|
JS_LockGCThing( cx, protoList[i] );
|
|
}
|
|
}
|
|
|
|
JSRuntime *CJSRuntime::GetRuntime( void ) const
|
|
{
|
|
return jsRuntime;
|
|
}
|
|
JSContext *CJSRuntime::GetContext( void ) const
|
|
{
|
|
return jsContext;
|
|
}
|
|
JSObject *CJSRuntime::GetObject( void ) const
|
|
{
|
|
return jsGlobal;
|
|
}
|
|
|
|
JSObject *CJSRuntime::GetPrototype( JSPrototypes protoNum ) const
|
|
{
|
|
JSObject *retVal = nullptr;
|
|
if( protoNum != JSP_COUNT )
|
|
{
|
|
retVal = protoList[protoNum];
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
JSObject *CJSRuntime::AcquireObject( IUEEntries iType, void *index )
|
|
{
|
|
JSObject *retVal = nullptr;
|
|
if( iType != IUE_COUNT && static_cast<size_t>( iType ) < objectList.size() )
|
|
{
|
|
retVal = FindAssociatedObject( iType, index );
|
|
if( retVal == nullptr )
|
|
{
|
|
retVal = MakeNewObject( iType );
|
|
if( retVal != nullptr )
|
|
{
|
|
objectList[iType][index] = retVal;
|
|
JS_SetPrivate( jsContext, retVal, index );
|
|
}
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
void CJSRuntime::ReleaseObject( IUEEntries iType, void *index )
|
|
{
|
|
JSOBJECTMAP_ITERATOR toSearch = objectList[iType].find( index );
|
|
if( toSearch != objectList[iType].end() )
|
|
{
|
|
JSObject *toRelease = ( *toSearch ).second;
|
|
JS_UnlockGCThing( jsContext, toRelease );
|
|
//JS_RemoveRoot( jsContext, &toRelease );
|
|
JS_SetPrivate( jsContext, toRelease, nullptr );
|
|
objectList[iType].erase( toSearch );
|
|
}
|
|
}
|
|
JSObject *CJSRuntime::FindAssociatedObject( IUEEntries iType, void *index )
|
|
{
|
|
JSObject *retVal = nullptr;
|
|
JSOBJECTMAP_CITERATOR toSearch = objectList[iType].find( index );
|
|
if( toSearch != objectList[iType].end() )
|
|
{
|
|
retVal = ( *toSearch ).second;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
JSObject *CJSRuntime::MakeNewObject( IUEEntries iType )
|
|
{
|
|
JSObject *toMake = nullptr;
|
|
switch( iType )
|
|
{
|
|
case IUE_RACE:
|
|
toMake = JS_NewObject( jsContext, &UOXRace_class, protoList[JSP_RACE], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CRace_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CRaceProperties );
|
|
break;
|
|
case IUE_CHAR:
|
|
toMake = JS_NewObject( jsContext, &UOXChar_class.base, protoList[JSP_CHAR], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineProperties( jsContext, toMake, CCharacterProps );
|
|
//JS_DefineFunctions( jsContext, toMake, CChar_Methods );
|
|
break;
|
|
case IUE_ITEM:
|
|
toMake = JS_NewObject( jsContext, &UOXItem_class.base, protoList[JSP_ITEM], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CItem_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CItemProps );
|
|
break;
|
|
case IUE_SOCK:
|
|
toMake = JS_NewObject( jsContext, &UOXSocket_class.base, protoList[JSP_SOCK], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CSocket_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CSocketProps );
|
|
break;
|
|
case IUE_GUILD:
|
|
toMake = JS_NewObject( jsContext, &UOXGuild_class, protoList[JSP_GUILD], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CGuild_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CGuildProperties );
|
|
break;
|
|
case IUE_REGION:
|
|
toMake = JS_NewObject( jsContext, &UOXRegion_class, protoList[JSP_REGION], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CRegion_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CRegionProperties );
|
|
break;
|
|
case IUE_SPAWNREGION:
|
|
toMake = JS_NewObject( jsContext, &UOXSpawnRegion_class, protoList[JSP_SPAWNREGION], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CSpawnRegion_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CSpawnRegionProperties );
|
|
break;
|
|
case IUE_PARTY:
|
|
toMake = JS_NewObject( jsContext, &UOXParty_class.base, protoList[JSP_PARTY], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CParty_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CPartyProperties );
|
|
break;
|
|
case IUE_ACCOUNT:
|
|
toMake = JS_NewObject( jsContext, &UOXAccount_class, protoList[JSP_ACCOUNT], jsGlobal );
|
|
if( toMake == nullptr )
|
|
return nullptr;
|
|
//JS_DefineFunctions( jsContext, toMake, CAccount_Methods );
|
|
//JS_DefineProperties( jsContext, toMake, CAccountProperties );
|
|
break;
|
|
default:
|
|
case IUE_COUNT:
|
|
return nullptr;
|
|
}
|
|
|
|
// DAMN! Using the deprecated function it works!
|
|
JS_LockGCThing( jsContext, toMake );
|
|
//JS_AddRoot( jsContext, &toMake );
|
|
return toMake;
|
|
}
|