-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow scripting of instances w/ persistent data
Some fixes for TC and changes overall Pass map object to hook via function arguments The map object is no longer stored inside the instance data table. Fix mistake in base64 decoder It was failing whenever it encountered a '=' character, which is completely valid. Make ElunaInstanceAI::Load always load something When it failed to load data, it was leaving nothing on the stack. Since subsequent code expected Load to always load something, this was causing issues. Now, when Load fails to load anything, it just leaves a new empty table on the stack. Also: the error messages for Load have been improved. Modify lua-marshal to allow saving of functions/userdata. Some additional code was needed to save functions due to the inclusion of a reference to _ENV within their upvalues (since Lua 5.2). During encoding, a placeholder is left where the _ENV reference would be. During decoding, a reference to the current _G is swapped with the placeholder. Make ElunaInstanceAI::Load re-initialize if data failed to load. Also improve error messages by not including the raw data. Improve storage format of upvalues Instead of storing the upvalues by name, store by index. A wrapper is still used in case the upvalue is nil, to prevent holes in the upvalues table. A special field in the upvalues table, "E", is used to store the index of the _ENV reference (if there was one). A reference to the current globals table is set as the upvalue upon decoding. Remove wrapping from upvalue storing, instead save amount of upvalues
Showing
14 changed files
with
1,564 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* Copyright (C) 2010 - 2015 Eluna Lua Engine <http://emudevs.com/> | ||
* This program is free software licensed under GPL version 3 | ||
* Please see the included DOCS/LICENSE.md for more information | ||
*/ | ||
|
||
#include "ElunaInstanceAI.h" | ||
#include "ElunaUtility.h" | ||
#include "lmarshal.h" | ||
|
||
|
||
void ElunaInstanceAI::Initialize() | ||
{ | ||
LOCK_ELUNA; | ||
|
||
ASSERT(!sEluna->HasInstanceData(instance)); | ||
|
||
// Create a new table for instance data. | ||
lua_State* L = sEluna->L; | ||
lua_newtable(L); | ||
sEluna->CreateInstanceData(instance); | ||
|
||
sEluna->OnInitialize(this); | ||
} | ||
|
||
void ElunaInstanceAI::Load(const char* data) | ||
{ | ||
LOCK_ELUNA; | ||
|
||
// If we get passed NULL (i.e. `Reload` was called) then use | ||
// the last known save data (or maybe just an empty string). | ||
if (!data) | ||
{ | ||
data = lastSaveData.c_str(); | ||
} | ||
else // Otherwise, copy the new data into our buffer. | ||
{ | ||
lastSaveData.assign(data); | ||
} | ||
|
||
if (data[0] == '\0') | ||
{ | ||
ASSERT(!sEluna->HasInstanceData(instance)); | ||
|
||
// Create a new table for instance data. | ||
lua_State* L = sEluna->L; | ||
lua_newtable(L); | ||
sEluna->CreateInstanceData(instance); | ||
|
||
sEluna->OnLoad(this); | ||
// Stack: (empty) | ||
return; | ||
} | ||
|
||
size_t decodedLength; | ||
const unsigned char* decodedData = ElunaUtil::DecodeData(data, &decodedLength); | ||
lua_State* L = sEluna->L; | ||
|
||
if (decodedData) | ||
{ | ||
// Stack: (empty) | ||
|
||
lua_pushcfunction(L, mar_decode); | ||
lua_pushlstring(L, (const char*)decodedData, decodedLength); | ||
// Stack: mar_decode, decoded_data | ||
|
||
// Call `mar_decode` and check for success. | ||
if (lua_pcall(L, 1, 1, 0) == 0) | ||
{ | ||
// Stack: data | ||
// Only use the data if it's a table. | ||
if (lua_istable(L, -1)) | ||
{ | ||
sEluna->CreateInstanceData(instance); | ||
// Stack: (empty) | ||
sEluna->OnLoad(this); | ||
// WARNING! lastSaveData might be different after `OnLoad` if the Lua code saved data. | ||
} | ||
else | ||
{ | ||
ELUNA_LOG_ERROR("Error while loading instance data: Expected data to be a table (type 5), got type %d instead", lua_type(L, -1)); | ||
lua_pop(L, 1); | ||
// Stack: (empty) | ||
|
||
Initialize(); | ||
} | ||
} | ||
else | ||
{ | ||
// Stack: error_message | ||
ELUNA_LOG_ERROR("Error while parsing instance data with lua-marshal: %s", lua_tostring(L, -1)); | ||
lua_pop(L, 1); | ||
// Stack: (empty) | ||
|
||
Initialize(); | ||
} | ||
|
||
delete[] decodedData; | ||
} | ||
else | ||
{ | ||
ELUNA_LOG_ERROR("Error while decoding instance data: Data is not valid base-64"); | ||
Initialize(); | ||
} | ||
} | ||
|
||
const char* ElunaInstanceAI::Save() const | ||
{ | ||
LOCK_ELUNA; | ||
lua_State* L = sEluna->L; | ||
// Stack: (empty) | ||
|
||
/* | ||
* Need to cheat because this method actually does modify this instance, | ||
* even though it's declared as `const`. | ||
* | ||
* Declaring virtual methods as `const` is BAD! | ||
* Don't dictate to children that their methods must be pure. | ||
*/ | ||
ElunaInstanceAI* self = const_cast<ElunaInstanceAI*>(this); | ||
|
||
lua_pushcfunction(L, mar_encode); | ||
sEluna->PushInstanceData(L, self, false); | ||
// Stack: mar_encode, instance_data | ||
|
||
if (lua_pcall(L, 1, 1, 0) != 0) | ||
{ | ||
// Stack: error_message | ||
ELUNA_LOG_ERROR("Error while saving: %s", lua_tostring(L, -1)); | ||
lua_pop(L, 1); | ||
return NULL; | ||
} | ||
|
||
// Stack: data | ||
size_t dataLength; | ||
const unsigned char* data = (const unsigned char*)lua_tolstring(L, -1, &dataLength); | ||
ElunaUtil::EncodeData(data, dataLength, self->lastSaveData); | ||
|
||
lua_pop(L, 1); | ||
// Stack: (empty) | ||
|
||
return lastSaveData.c_str(); | ||
} | ||
|
||
uint32 ElunaInstanceAI::GetData(uint32 key) const | ||
{ | ||
LOCK_ELUNA; | ||
lua_State* L = sEluna->L; | ||
// Stack: (empty) | ||
|
||
sEluna->PushInstanceData(L, const_cast<ElunaInstanceAI*>(this), false); | ||
// Stack: instance_data | ||
|
||
Eluna::Push(L, key); | ||
// Stack: instance_data, key | ||
|
||
lua_gettable(L, -2); | ||
// Stack: instance_data, value | ||
|
||
uint32 value = Eluna::CHECKVAL<uint32>(L, -1, 0); | ||
lua_pop(L, 2); | ||
// Stack: (empty) | ||
|
||
return value; | ||
} | ||
|
||
void ElunaInstanceAI::SetData(uint32 key, uint32 value) | ||
{ | ||
LOCK_ELUNA; | ||
lua_State* L = sEluna->L; | ||
// Stack: (empty) | ||
|
||
sEluna->PushInstanceData(L, this, false); | ||
// Stack: instance_data | ||
|
||
Eluna::Push(L, key); | ||
Eluna::Push(L, value); | ||
// Stack: instance_data, key, value | ||
|
||
lua_settable(L, -3); | ||
// Stack: instance_data | ||
|
||
lua_pop(L, 1); | ||
// Stack: (empty) | ||
} | ||
|
||
uint64 ElunaInstanceAI::GetData64(uint32 key) const | ||
{ | ||
LOCK_ELUNA; | ||
lua_State* L = sEluna->L; | ||
// Stack: (empty) | ||
|
||
sEluna->PushInstanceData(L, const_cast<ElunaInstanceAI*>(this), false); | ||
// Stack: instance_data | ||
|
||
Eluna::Push(L, key); | ||
// Stack: instance_data, key | ||
|
||
lua_gettable(L, -2); | ||
// Stack: instance_data, value | ||
|
||
uint64 value = Eluna::CHECKVAL<uint64>(L, -1, 0); | ||
lua_pop(L, 2); | ||
// Stack: (empty) | ||
|
||
return value; | ||
} | ||
|
||
void ElunaInstanceAI::SetData64(uint32 key, uint64 value) | ||
{ | ||
LOCK_ELUNA; | ||
lua_State* L = sEluna->L; | ||
// Stack: (empty) | ||
|
||
sEluna->PushInstanceData(L, this, false); | ||
// Stack: instance_data | ||
|
||
Eluna::Push(L, key); | ||
Eluna::Push(L, value); | ||
// Stack: instance_data, key, value | ||
|
||
lua_settable(L, -3); | ||
// Stack: instance_data | ||
|
||
lua_pop(L, 1); | ||
// Stack: (empty) | ||
} |
Oops, something went wrong.