Refactor code for invoke_lua_source and world.init. Also, add compile_commands.json to luprex

This commit is contained in:
2025-06-16 19:58:26 -04:00
parent f150b14d30
commit 80ff7d7d92
14 changed files with 279 additions and 331 deletions

View File

@@ -54,6 +54,21 @@ ALuprexGameModeBase::~ALuprexGameModeBase()
// //
uint32 ALuprexGameModeBase::Run() { uint32 ALuprexGameModeBase::Run() {
FlxLockedWrapper lockedwrap(LockableWrapper); FlxLockedWrapper lockedwrap(LockableWrapper);
if (lockedwrap->get_rescan_lua_source(lockedwrap.Get()))
{
drvutil::ostringstream srcpak;
std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak);
if (!srcpakerr.empty())
{
FString FMessage((const UTF8CHAR *)(srcpakerr.c_str()));
UE_LOG(LogLuprexIntegration, Error, TEXT("Trying to read Lua source: %s"), *FMessage);
}
else
{
std::string_view srcpakv = srcpak.view();
lockedwrap->play_access(lockedwrap.Get(), AccessKind::INVOKE_LUA_SOURCE, 0, srcpakv.size(), srcpakv.data(), nullptr, nullptr);
}
}
Sockets->Update(lockedwrap); Sockets->Update(lockedwrap);
lockedwrap->play_update(lockedwrap.Get(), EngineSeconds); lockedwrap->play_update(lockedwrap.Get(), EngineSeconds);
Sockets->Update(lockedwrap); Sockets->Update(lockedwrap);
@@ -290,19 +305,7 @@ void ALuprexGameModeBase::InitializeGlobalState()
// If wrapper is initialized, try to initialize the luprex engine. // If wrapper is initialized, try to initialize the luprex engine.
if (w->play_initialize != nullptr) if (w->play_initialize != nullptr)
{ {
drvutil::ostringstream srcpak; w->play_initialize(w.Get(), "lpxclient", "");
std::string srcpakerr = drvutil::package_lua_source(LUPREX_ROOT_PATH, &srcpak);
if (!srcpakerr.empty())
{
FString FMessage((const UTF8CHAR *)(srcpakerr.c_str()));
UE_LOG(LogLuprexIntegration, Error, TEXT("Trying to read Lua source: %s"), *FMessage);
}
else
{
std::string_view srcpakv = srcpak.view();
char* argv[1];
argv[0] = const_cast<char*>("lpxclient");
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
if (w->error[0]) if (w->error[0])
{ {
FString FMessage((const UTF8CHAR *)w->error); FString FMessage((const UTF8CHAR *)w->error);
@@ -313,7 +316,6 @@ void ALuprexGameModeBase::InitializeGlobalState()
Playing = true; Playing = true;
} }
} }
}
// If we successfully created a luprex engine, create a socket system and a worker thread. // If we successfully created a luprex engine, create a socket system and a worker thread.
if (Playing) { if (Playing) {

View File

@@ -266,6 +266,7 @@ def build_intellisense_database_for_clangd(force):
shell(INTEGRATION, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex IntegrationEditor {OS} DebugGame {INTEGRATION}/Integration.uproject -mode=GenerateClangDatabase -OutputDir={UNREALENGINE}/.vscode") shell(INTEGRATION, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex IntegrationEditor {OS} DebugGame {INTEGRATION}/Integration.uproject -mode=GenerateClangDatabase -OutputDir={UNREALENGINE}/.vscode")
shell(UNREALENGINE, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex UnrealEditor {OS} DebugGame -mode=GenerateClangDatabase -OutputDir={UNREALENGINE}/.vscode") shell(UNREALENGINE, f"{UNREALENGINE}/Engine/Build/BatchFiles/{BUILD_BAT} -waitmutex UnrealEditor {OS} DebugGame -mode=GenerateClangDatabase -OutputDir={UNREALENGINE}/.vscode")
shell(INTEGRATION, f"cat {UNREALENGINE}/.vscode/compile_commands.json >> {INTEGRATION}/.vscode/compile_commands.json") shell(INTEGRATION, f"cat {UNREALENGINE}/.vscode/compile_commands.json >> {INTEGRATION}/.vscode/compile_commands.json")
shell(INTEGRATION, f"cat {INTEGRATION}/luprex/build/{OS}/compile_commands.json >> {INTEGRATION}/.vscode/compile_commands.json")
except Exception as e: except Exception as e:
error = e error = e
else: else:

View File

@@ -5,11 +5,11 @@
####################################################################### #######################################################################
ifneq "" "$(findstring -linux-,$(MAKE_HOST))" ifneq "" "$(findstring -linux-,$(MAKE_HOST))"
OS=Linux OS:=Linux
else ifneq "" "$(VSINSTALLDIR)" else ifneq "" "$(VSINSTALLDIR)"
OS=Windows OS:=Windows
else else
OS="" OS:=""
endif endif
ifeq "$(OS)" "" ifeq "$(OS)" ""
@@ -18,91 +18,6 @@ endif
$(info Building for $(OS)...) $(info Building for $(OS)...)
#######################################################################
##
## List of all OBJ files
##
#######################################################################
OBJ_ERIS=\
build/$(OS)/eris/lapi.obj \
build/$(OS)/eris/lcode.obj \
build/$(OS)/eris/lctype.obj \
build/$(OS)/eris/ldebug.obj \
build/$(OS)/eris/ldo.obj \
build/$(OS)/eris/ldump.obj \
build/$(OS)/eris/lfunc.obj \
build/$(OS)/eris/lgc.obj \
build/$(OS)/eris/llex.obj \
build/$(OS)/eris/lmem.obj \
build/$(OS)/eris/lobject.obj \
build/$(OS)/eris/lopcodes.obj \
build/$(OS)/eris/lparser.obj \
build/$(OS)/eris/lstate.obj \
build/$(OS)/eris/lstring.obj \
build/$(OS)/eris/ltable.obj \
build/$(OS)/eris/ltm.obj \
build/$(OS)/eris/lundump.obj \
build/$(OS)/eris/lvm.obj \
build/$(OS)/eris/lzio.obj \
build/$(OS)/eris/lauxlib.obj \
build/$(OS)/eris/lbaselib.obj \
build/$(OS)/eris/lbitlib.obj \
build/$(OS)/eris/lcorolib.obj \
build/$(OS)/eris/ldblib.obj \
build/$(OS)/eris/liolib.obj \
build/$(OS)/eris/lmathlib.obj \
build/$(OS)/eris/loslib.obj \
build/$(OS)/eris/lstrlib.obj \
build/$(OS)/eris/ltablib.obj \
build/$(OS)/eris/loadlib.obj \
build/$(OS)/eris/linit.obj \
build/$(OS)/eris/eris.obj \
OBJ_CORE=\
build/$(OS)/core/invocation.obj\
build/$(OS)/core/spookyv2.obj\
build/$(OS)/core/eng-malloc.obj\
build/$(OS)/core/debugcollector.obj\
build/$(OS)/core/drivenengine.obj\
build/$(OS)/core/util.obj\
build/$(OS)/core/luastack.obj\
build/$(OS)/core/traceback.obj\
build/$(OS)/core/planemap.obj\
build/$(OS)/core/pprint.obj\
build/$(OS)/core/luaconsole.obj\
build/$(OS)/core/luavector.obj\
build/$(OS)/core/idalloc.obj\
build/$(OS)/core/globaldb.obj\
build/$(OS)/core/sched.obj\
build/$(OS)/core/http.obj\
build/$(OS)/core/json.obj\
build/$(OS)/core/table.obj\
build/$(OS)/core/luasnap.obj\
build/$(OS)/core/animqueue.obj\
build/$(OS)/core/streambuffer.obj\
build/$(OS)/core/source.obj\
build/$(OS)/core/world-core.obj\
build/$(OS)/core/world-accessor.obj\
build/$(OS)/core/world-difftab.obj\
build/$(OS)/core/world-diffxmit.obj\
build/$(OS)/core/world-pairtab.obj\
build/$(OS)/core/world-testing.obj\
build/$(OS)/core/lpxserver.obj\
build/$(OS)/core/lpxclient.obj\
build/$(OS)/core/eng-tests.obj\
build/$(OS)/core/printbuffer.obj\
build/$(OS)/core/serializelua.obj\
OBJ_DRV=\
build/$(OS)/drv/driver.obj\
build/$(OS)/drv/drvutil.obj\
build/$(OS)/drv/osdrvutil.obj\
build/$(OS)/drv/sslutil.obj\
build/$(OS)/drv/readline.obj\
####################################################################### #######################################################################
## ##
## Make rules for linux ## Make rules for linux
@@ -110,17 +25,20 @@ OBJ_DRV=\
####################################################################### #######################################################################
ifeq "$(OS)" "Linux" ifeq "$(OS)" "Linux"
OPT=-g -O0 OPT:=-g -O0
LUPREX_EXE=luprex LUPREX_EXE:=luprex
LUPREXLIB_DLL=luprexlib.so LUPREXLIB_DLL:=luprexlib.so
LUPREXSTATIC_EXE=luprexstatic LUPREXSTATIC_EXE:=luprexstatic
COMPILE=g++ -Wall $(OPT) -std=c++20 -fvisibility=hidden -c -MMD -fPIC -o COMPILE:=g++ -Wall $(OPT) -std=c++20 -fvisibility=hidden -c -MMD -fPIC -o
LINKDLL=g++ -Wall $(OPT) -std=c++20 -export-dynamic -Wl,--no-allow-shlib-undefined -Wl,-z,defs -shared -o LINKDLL:=g++ -Wall $(OPT) -std=c++20 -export-dynamic -Wl,--no-allow-shlib-undefined -Wl,-z,defs -shared -o
LINKEXE=g++ -Wall $(OPT) -std=c++20 -export-dynamic -o LINKEXE:=g++ -Wall $(OPT) -std=c++20 -export-dynamic -o
MAKEDEPS=true MAKEDEPS:=true
OPENSSL_INCLUDE=-I./ext/openssl-3.0.1/inc LIBS:=-L./ext/openssl-3.0.1/lib/linux -lssl -lcrypto -ldl
LUA_FLAGS=-DLUA_USE_APICHECK -DLUA_USE_POSIX CCJSON="build/$(OS)/compile_commands.json"
LIBS=-L./ext/openssl-3.0.1/lib/linux -lssl -lcrypto -ldl
FLAGS_ERIS:=-DLUA_USE_APICHECK -DLUA_USE_POSIX
FLAGS_CORE:=-I./ext/eris-master/src -I./cpp/wrap -I./cpp/core -I./ext
FLAGS_DRV:=-I./ext/openssl-3.1.0/inc -I./src/drv -I./ext
endif endif
####################################################################### #######################################################################
@@ -133,26 +51,60 @@ ifeq "$(OS)" "Windows"
ifeq "" "$(VSINSTALLDIR)" ifeq "" "$(VSINSTALLDIR)"
$(error You must use vcvars64.bat to set up the visual studio environment variables) $(error You must use vcvars64.bat to set up the visual studio environment variables)
endif endif
OPT=/Od /Zi OPT:=/Od /Zi
LUPREX_EXE=luprex.exe LUPREX_EXE:=luprex.exe
LUPREXLIB_DLL=luprexlib.dll LUPREXLIB_DLL:=luprexlib.dll
LUPREXSTATIC_EXE=luprexstatic.exe LUPREXSTATIC_EXE:=luprexstatic.exe
COMPILE=CL $(OPT) /std:c++20 /EHsc /nologo /MD /TP /c /Fo: COMPILE:=CL $(OPT) /std:c++20 /EHsc /nologo /MD /TP /c /Fo:
LINKDLL=CL $(OPT) /std:c++20 /EHsc /nologo /LDd /Fe: LINKDLL:=CL $(OPT) /std:c++20 /EHsc /nologo /LDd /Fe:
LINKEXE=CL $(OPT) /std:c++20 /EHsc /nologo /Fe: LINKEXE:=CL $(OPT) /std:c++20 /EHsc /nologo /Fe:
MAKEDEPS=g++ -Wall -std=c++20 -MMD -E -o MAKEDEPS:=g++ -Wall -std=c++20 -MMD -E -o
OPENSSL_INCLUDE=-I./ext/openssl-3.1.0/inc LIBS:=ext/openssl-3.1.0/lib/visual/libcrypto.lib ext/openssl-3.1.0/lib/visual/libssl.lib ws2_32.lib crypt32.lib cryptui.lib user32.lib advapi32.lib
LUA_FLAGS=-DLUA_USE_APICHECK -DLUA_COMPAT_ALL CCJSON="build/$(OS)/compile_commands.json"
LIBS=ext/openssl-3.1.0/lib/visual/libcrypto.lib ext/openssl-3.1.0/lib/visual/libssl.lib ws2_32.lib crypt32.lib cryptui.lib user32.lib advapi32.lib
FLAGS_ERIS:=-DLUA_USE_APICHECK -DLUA_COMPAT_ALL
FLAGS_CORE:=-I./ext/eris-master/src -I./cpp/wrap -I./cpp/core -I./ext
FLAGS_DRV:=-I./ext/openssl-3.1.0/inc -I./src/drv -I./ext
endif endif
#######################################################################
##
## List of all source files
##
#######################################################################
BASE_ERIS := \
lapi lcode lctype ldebug ldo ldump lfunc lgc llex lmem lobject lopcodes \
lparser lstate lstring ltable ltm lundump lvm lzio \
lauxlib lbaselib lbitlib lcorolib ldblib liolib lmathlib loslib \
lstrlib ltablib loadlib linit eris
BASE_CORE := \
invocation spookyv2 eng-malloc debugcollector drivenengine util luastack \
traceback planemap pprint luaconsole luavector idalloc globaldb sched http \
json table luasnap animqueue streambuffer source world-core world-accessor \
world-difftab world-diffxmit world-pairtab world-testing lpxserver lpxclient \
eng-tests printbuffer serializelua
BASE_DRV := driver drvutil osdrvutil sslutil readline
#######################################################################
##
## Generate lists of OBJ files.
##
#######################################################################
OBJ_ERIS := $(patsubst %,build/$(OS)/eris/%.obj,$(BASE_ERIS))
OBJ_CORE := $(patsubst %,build/$(OS)/core/%.obj,$(BASE_CORE))
OBJ_DRV := $(patsubst %,build/$(OS)/drv/%.obj,$(BASE_DRV))
####################################################################### #######################################################################
## ##
## Make Rules ## Make Rules
## ##
####################################################################### #######################################################################
all: build/$(OS)/$(LUPREX_EXE) build/$(OS)/$(LUPREXSTATIC_EXE) build/$(OS)/$(LUPREXLIB_DLL) all: build/$(OS)/$(LUPREX_EXE) build/$(OS)/$(LUPREXSTATIC_EXE) build/$(OS)/$(LUPREXLIB_DLL) $(CCJSON)
build/$(OS)/DIRECTORY: build/$(OS)/DIRECTORY:
mkdir -p build/$(OS)/core build/$(OS)/eris build/$(OS)/drv mkdir -p build/$(OS)/core build/$(OS)/eris build/$(OS)/drv
@@ -168,23 +120,29 @@ build/$(OS)/$(LUPREXLIB_DLL): $(OBJ_ERIS) $(OBJ_CORE)
$(LINKDLL) $@ $^ $(LINKDLL) $@ $^
build/$(OS)/eris/%.obj: ext/eris-master/src/%.c build/$(OS)/DIRECTORY build/$(OS)/eris/%.obj: ext/eris-master/src/%.c build/$(OS)/DIRECTORY
$(MAKEDEPS) $@d $(LUA_FLAGS) $< $(MAKEDEPS) $@d $(FLAGS_ERIS) $<
$(COMPILE) $@ $(LUA_FLAGS) $< $(COMPILE) $@ $(FLAGS_ERIS) $<
build/$(OS)/core/%.obj: cpp/core/%.cpp build/$(OS)/DIRECTORY build/$(OS)/core/%.obj: cpp/core/%.cpp build/$(OS)/DIRECTORY
$(MAKEDEPS) $@d -I./ext/eris-master/src -I./cpp/wrap -I./cpp/core -I./ext $< $(MAKEDEPS) $@d $(FLAGS_CORE) $<
$(COMPILE) $@ -I./ext/eris-master/src -I./cpp/wrap -I./cpp/core -I./ext $< $(COMPILE) $@ $(FLAGS_CORE) $<
build/$(OS)/drv/%.obj: cpp/drv/%.cpp build/$(OS)/DIRECTORY build/$(OS)/drv/%.obj: cpp/drv/%.cpp build/$(OS)/DIRECTORY
$(MAKEDEPS) $@d $(OPENSSL_INCLUDE) -I./src/drv -I./ext $< $(MAKEDEPS) $@d $(FLAGS_DRV) $<
$(COMPILE) $@ $(OPENSSL_INCLUDE) -I./src/drv -I./ext $< $(COMPILE) $@ $(FLAGS_DRV) $<
$(CCJSON): Makefile add-compile-commands.py
rm -rf $(CCJSON)
python3 ./add-compile-commands.py "$(CCJSON)" "$(COMPILE)" "$(FLAGS_ERIS)" "build/$(OS)/eris/FILE.obj" "ext/eris-master/src/FILE.c" $(BASE_ERIS)
python3 ./add-compile-commands.py "$(CCJSON)" "$(COMPILE)" "$(FLAGS_CORE)" "build/$(OS)/cpp/FILE.obj" "cpp/core/FILE.cpp" $(BASE_CORE)
python3 ./add-compile-commands.py "$(CCJSON)" "$(COMPILE)" "$(FLAGS_DRV)" "build/$(OS)/drv/FILE.obj" "cpp/drv/FILE.cpp" $(BASE_DRV)
clean: clean:
rm -f luprex* luprex*.* *.pdb rm -f *.pdb
rm -rf build rm -rf build
clean-os: clean-os:
rm -f luprex* luprex*.* *.pdb build/$(OS)/* build/$(OS)/*/* rm -f *.pdb build/$(OS)/* build/$(OS)/*/*
####################################################################### #######################################################################
## ##

View File

@@ -0,0 +1,23 @@
import sys, os
LUPREXDIR=os.path.dirname(os.path.abspath(sys.argv[0]))
JSON=sys.argv[1]
COMPILE=sys.argv[2]
FLAGS=sys.argv[3]
OBJPAT=sys.argv[4]
CPAT=sys.argv[5]
OBJECTS=sys.argv[6:]
with open(JSON, "a") as cc:
for base in OBJECTS:
obj = LUPREXDIR + "/" + OBJPAT.replace("FILE", base)
file = LUPREXDIR + "/" + CPAT.replace("FILE", base)
flags = FLAGS.replace("-I./", f"-I{LUPREXDIR}/")
cc.write('{\n')
cc.write(f' "file" : "{file}",\n')
cc.write(f' "command" : "{COMPILE} {obj} {flags} {file}",\n')
cc.write(f' "directory" : "{LUPREXDIR}"\n')
cc.write('}\n')

View File

@@ -436,9 +436,6 @@ void DrivenEngine::drv_get_animation_queues(uint32_t count, const int64_t *ids,
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
void DrivenEngine::drv_initialize(uint32_t srcpklen, const char *srcpk, int argc, char **argv) {
event_init(std::string_view(srcpk, srcpklen), argc, argv);
}
void DrivenEngine::drv_clear_new_outgoing() { void DrivenEngine::drv_clear_new_outgoing() {
new_outgoing_.clear(); new_outgoing_.clear();
@@ -569,7 +566,7 @@ static void drv_get_animation_queues(EngineWrapper *w, uint32_t count, const int
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
static void play_initialize(EngineWrapper *w, uint32_t argc, char **argv, uint32_t srcpklen, const char *srcpk, const char *logfn) { static void play_initialize(EngineWrapper *w, const char *engtype, const char *logfn) {
if (w->engine != nullptr) { if (w->engine != nullptr) {
return reset_wrapper(w, "Cannot initialize wrapper, it's already initialized."); return reset_wrapper(w, "Cannot initialize wrapper, it's already initialized.");
} }
@@ -590,63 +587,31 @@ static void play_initialize(EngineWrapper *w, uint32_t argc, char **argv, uint32
// If we have a logfile, then log this initialization. // If we have a logfile, then log this initialization.
if (w->wlog != nullptr) { if (w->wlog != nullptr) {
w->wlog->write_cmd_hash(PLAY_INITIALIZE, eng::memhash()); w->wlog->write_cmd_hash(PLAY_INITIALIZE, eng::memhash());
w->wlog->write_uint32(argc); w->wlog->write_string(engtype);
for (uint32_t i = 0; i < argc; i++) {
w->wlog->write_string(argv[i]);
}
w->wlog->write_string(std::string_view(srcpk, srcpklen));
w->wlog->flush(); w->wlog->flush();
} }
// Create the engine of the appropriate type. // Create the engine of the appropriate type.
if (argc < 1) { w->engine = make_engine(engtype);
std::ostringstream oss;
oss << "Must pass an engine type on the command line. Known types:\n";
for (auto reg = DrivenEngineReg::All; reg != nullptr; reg=reg->next) {
oss << " " << reg->name << std::endl;
}
std::string err = oss.str();
return reset_wrapper(w, err.c_str());
}
w->engine = make_engine(argv[0]);
if (w->engine == nullptr) { if (w->engine == nullptr) {
return reset_wrapper(w, "No such driven engine type: %s", argv[0]); return reset_wrapper(w, "No such driven engine type: %s", engtype);
} }
// Call the engine initialization sequence.
w->engine->drv_initialize(srcpklen, srcpk, argc - 1, argv + 1);
} }
static void replay_initialize(EngineWrapper *w) { static void replay_initialize(EngineWrapper *w) {
assert(w->rlog != nullptr); assert(w->rlog != nullptr);
std::vector<std::string> argvstr; std::string engtype = w->rlog->read_string();
uint32_t argc = w->rlog->read_uint32();
for (uint32_t i = 0; i < argc; i++) {
argvstr.push_back(w->rlog->read_string());
}
std::string srcpk = w->rlog->read_string();
if (!w->rlog->good()) { if (!w->rlog->good()) {
return reset_wrapper(w, "replay log corrupt in replay_initialize"); return reset_wrapper(w, "replay log corrupt in replay_initialize");
} }
// We need to convert the argument vector from an array
// of C++ strings into the canonical argc, argv format.
std::vector<char *> argvec;
for (uint32_t i = 0; i < argc; i++) {
argvec.push_back(&argvstr[i][0]);
}
char **argv = &argvec[0];
// Create the engine. // Create the engine.
w->engine = make_engine(argv[0]); w->engine = make_engine(engtype.c_str());
if (w->engine == nullptr) { if (w->engine == nullptr) {
return reset_wrapper(w, "No such driven engine type: %s", argvstr[0]); return reset_wrapper(w, "No such driven engine type: %s", engtype.c_str());
} }
w->engine->drv_initialize(srcpk.size(), srcpk.c_str(), argc - 1, argv + 1);
} }

View File

@@ -142,11 +142,6 @@ public:
// //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
// The init callback. You may override this in a subclass.
// This will be called once at program initialization.
//
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) = 0;
// The call-function callback. This is invoked whenever drv_access // The call-function callback. This is invoked whenever drv_access
// is called. This is the main entry point for "general" access into the // is called. This is the main entry point for "general" access into the
// DrivenEngine. The datapk parameter can contain any arbitrary data needed // DrivenEngine. The datapk parameter can contain any arbitrary data needed
@@ -289,7 +284,6 @@ public:
void drv_get_tangibles_near(int64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids); void drv_get_tangibles_near(int64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids);
void drv_get_animation_queues(uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings); void drv_get_animation_queues(uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings);
void drv_initialize(uint32_t srcpklen, const char *srcpk, int argc, char **argv);
void drv_clear_new_outgoing(); void drv_clear_new_outgoing();
void drv_sent_outgoing(uint32_t chid, uint32_t nbytes); void drv_sent_outgoing(uint32_t chid, uint32_t nbytes);
void drv_recv_incoming(uint32_t chid, uint32_t nbytes, const char *bytes); void drv_recv_incoming(uint32_t chid, uint32_t nbytes, const char *bytes);

View File

@@ -22,21 +22,12 @@ static void dump_lines(StreamBuffer *in, StreamBuffer *out, int chid) {
} }
} }
// This test is the minimal possible DrivenEngine.
class DriverStubTest : public DrivenEngine {
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) override {
stop_driver();
}
virtual void event_update() override {
}
};
// This test connects to a public webserver and prints // This test connects to a public webserver and prints
// the output from the server. // the output from the server.
class DriverWebServerTest : public DrivenEngine { class DriverWebServerTest : public DrivenEngine {
public: public:
eng::vector<SharedChannel> channels_; eng::vector<SharedChannel> channels_;
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) override { DriverWebServerTest() {
SharedChannel ch = new_outgoing_channel("cert:stanford.edu:443"); SharedChannel ch = new_outgoing_channel("cert:stanford.edu:443");
ch->out()->write_bytes("GET https://stanford.edu/xbanankjdsh.html HTTP/1.1\n\n"); ch->out()->write_bytes("GET https://stanford.edu/xbanankjdsh.html HTTP/1.1\n\n");
channels_.emplace_back(std::move(ch)); channels_.emplace_back(std::move(ch));
@@ -62,7 +53,7 @@ public:
class DriverDNSFailTest : public DrivenEngine { class DriverDNSFailTest : public DrivenEngine {
public: public:
eng::vector<SharedChannel> channels_; eng::vector<SharedChannel> channels_;
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) override { DriverDNSFailTest() {
SharedChannel ch = new_outgoing_channel("akjsdkajshdakjshd.alk:80"); SharedChannel ch = new_outgoing_channel("akjsdkajshdakjshd.alk:80");
ch->out()->write_bytes("GET http://stanford.edu/index.html HTTP/1.1\n\n"); ch->out()->write_bytes("GET http://stanford.edu/index.html HTTP/1.1\n\n");
channels_.emplace_back(std::move(ch)); channels_.emplace_back(std::move(ch));
@@ -89,7 +80,7 @@ class DriverPrintClockTest : public DrivenEngine {
public: public:
int count_; int count_;
double last_clock_; double last_clock_;
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) override { DriverPrintClockTest() {
count_ = 0; count_ = 0;
last_clock_ = 0.0; last_clock_ = 0.0;
} }
@@ -111,21 +102,29 @@ public:
class RunUnitTests : public DrivenEngine { class RunUnitTests : public DrivenEngine {
private: public:
UniqueWorld world_; UniqueWorld world_;
void event_init(std::string_view srcpk, int argc, char *argv[]) override RunUnitTests() {
{
world_.reset(new World(WORLD_TYPE_MASTER)); world_.reset(new World(WORLD_TYPE_MASTER));
world_->update_source(srcpk);
world_->run_unittests();
stop_driver(); stop_driver();
rescan_lua_source();
}
virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override {
switch (kind) {
case AccessKind::INVOKE_LUA_SOURCE: {
world_->update_source(datapk);
world_->run_unittests();
break;
}
default: break;
}
} }
void event_update() override {} void event_update() override {}
}; };
DrivenEngineDefine("driverstubtest", DriverStubTest);
DrivenEngineDefine("driverwebservertest", DriverWebServerTest); DrivenEngineDefine("driverwebservertest", DriverWebServerTest);
DrivenEngineDefine("driverdnsfailtest", DriverDNSFailTest); DrivenEngineDefine("driverdnsfailtest", DriverDNSFailTest);
DrivenEngineDefine("driverprintclocktest", DriverPrintClockTest); DrivenEngineDefine("driverprintclocktest", DriverPrintClockTest);

View File

@@ -28,6 +28,7 @@ enum class AccessKind {
INVOKE_TICK, INVOKE_TICK,
INVOKE_LUA_SOURCE, INVOKE_LUA_SOURCE,
PROBE_LUA_CALL, PROBE_LUA_CALL,
CONNECT_TO_SERVER,
}; };
class DrivenEngine; class DrivenEngine;
@@ -154,15 +155,11 @@ struct EngineWrapper {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Create the driven engine. argc and argv allow you to specify what // Create the driven engine. You must specify the type of engine you
// kind of engine you want. You must pass in the initial state of the lua // want (typically 'lpxserver' or 'lpxclient'), and a filename for the
// source, if you have any. You may optionally also specify a replay log. // logfile.
// If you don't want to create a replay log, pass a null pointer.
// //
// Check to see if the error buffer contains a message after calling void (*play_initialize)(EngineWrapper *w, const char *engtype, const char *logfn);
// this function.
//
void (*play_initialize)(EngineWrapper *w, uint32_t argc, char **argv, uint32_t srcpklen, const char *srcpk, const char *logfn);
// Clear the list of recently-opened channels. You are meant to fetch // Clear the list of recently-opened channels. You are meant to fetch
// new outgoing channels using get_new_outgoing, then you call // new outgoing channels using get_new_outgoing, then you call

View File

@@ -22,6 +22,12 @@ public:
eng::vector<Invocation> delayed_invocations_; eng::vector<Invocation> delayed_invocations_;
public: public:
LpxClient() {
set_console_prompt(console_.get_prompt());
set_initial_state_standalone();
}
void set_initial_state_connect(const eng::string &hostspec) { void set_initial_state_connect(const eng::string &hostspec) {
// Create the world model. // Create the world model.
world_.reset(new World(WORLD_TYPE_PREDICTIVE)); world_.reset(new World(WORLD_TYPE_PREDICTIVE));
@@ -46,21 +52,16 @@ public:
print_channeler_.reset(); print_channeler_.reset();
} }
void set_initial_state_standalone(std::string_view srcpk) { void set_initial_state_standalone() {
// Create the world model. // Create the world model.
world_.reset(new World(WORLD_TYPE_MASTER)); world_.reset(new World(WORLD_TYPE_MASTER));
// Update the source code of the master model.
world_->update_source(srcpk);
// Make sure the channel is empty. // Make sure the channel is empty.
channel_.reset(); channel_.reset();
// Create the standalone actor. // Create the standalone actor.
actor_id_ = world_->create_login_actor(); actor_id_ = world_->create_login_actor();
// TODO: initialize the standalone actor.
// Clear the unack command queue. // Clear the unack command queue.
unack_.clear(); unack_.clear();
@@ -69,8 +70,10 @@ public:
// Reset the print channeler // Reset the print channeler
print_channeler_.reset(); print_channeler_.reset();
}
// Trigger lua source loading.
rescan_lua_source();
}
// When the world is in synchronous mode, there's no // When the world is in synchronous mode, there's no
// snapshot, and the commands in the unack queue are not // snapshot, and the commands in the unack queue are not
@@ -96,37 +99,15 @@ public:
} }
void abandon_server() { void abandon_server() {
// When we abandon the server, we leave the world model if (channel_)
// hanging around to preserve the invariant that there {
// is always a world model. Then, we trigger a rescan set_initial_state_standalone();
// of the lua source. When the lua source shows up, then
// we will create a standalone model to replace the client
// model.
channel_.reset();
rescan_lua_source();
} }
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) {
// Put the world into the starting state.
set_initial_state_standalone(srcpk);
// Set the console prompt
set_console_prompt(console_.get_prompt());
} }
void send_invocation(const Invocation &inv) { void send_invocation(const Invocation &inv) {
if (channel_ == nullptr) { if (channel_ == nullptr) {
if ((!world_->is_authoritative()) && (inv.kind() == AccessKind::INVOKE_LUA_SOURCE)) {
// We have a client model, but no client connection. That means we're
// in the process of shutting down a client model. The client model
// is supposed to linger until the lua source is reread. Once we have
// the lua source, we're supposed to throw out the client model and
// create a standalone model.
set_initial_state_standalone(inv.datapack());
} else {
world_->invoke(inv); world_->invoke(inv);
}
} else { } else {
world_to_asynchronous(); world_to_asynchronous();
world_->invoke(inv); world_->invoke(inv);
@@ -247,14 +228,27 @@ public:
} }
virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override { virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override {
if (place_id == 0) place_id = actor_id_;
switch (kind) { switch (kind) {
case AccessKind::INVOKE_LUA_CALL:
case AccessKind::INVOKE_LUA_EXPR:
case AccessKind::INVOKE_FLUSH_PRINTS:
case AccessKind::INVOKE_TICK:
case AccessKind::INVOKE_LUA_SOURCE: {
delayed_invocations_.emplace_back(kind, actor_id_, place_id, datapk);
break;
}
case AccessKind::PROBE_LUA_CALL: { case AccessKind::PROBE_LUA_CALL: {
world_to_asynchronous(); world_to_asynchronous();
world_->probe_lua_call(actor_id_, place_id, datapk, retpk); world_->probe_lua_call(actor_id_, place_id, datapk, retpk);
break; break;
} }
case AccessKind::CONNECT_TO_SERVER: {
set_initial_state_connect(util::ss("nocert:", datapk, ":8085"));
break;
}
default: { default: {
delayed_invocations_.emplace_back(kind, actor_id_, place_id, datapk); stdostream() << "Invalid event_access: " << int(kind) << std::endl;
} }
} }
} }

View File

@@ -29,24 +29,19 @@ public:
PrintChanneler print_channeler_; PrintChanneler print_channeler_;
HttpChannelMap http_client_channels_; HttpChannelMap http_client_channels_;
HttpChannelVec http_server_channels_; HttpChannelVec http_server_channels_;
int64_t admin_id_;
int next_diff_chan_;
double next_tick_;
eng::vector<Invocation> delayed_invocations_; eng::vector<Invocation> delayed_invocations_;
int64_t admin_id_ = 0;
double next_tick_ = 0;
public: public:
virtual void event_init(std::string_view srcpk, int argc, char *argv[]) override { LpxServer()
{
// Create the master world model. // Create the master world model.
master_.reset(new World(WORLD_TYPE_MASTER)); master_.reset(new World(WORLD_TYPE_MASTER));
// Update the source code of the master model. // Create the admin actor. Note: there isn't any 'init' function yet.
master_->update_source(srcpk);
// Create an actor for administrative commands.
admin_id_ = master_->create_login_actor(); admin_id_ = master_->create_login_actor();
// TODO: initialize the admin actor.
// Print out admin ID for debugging purposes. // Print out admin ID for debugging purposes.
stdostream() << "Admin actor id = " << admin_id_ << std::endl; stdostream() << "Admin actor id = " << admin_id_ << std::endl;
@@ -62,8 +57,8 @@ public:
// Export stuff to the graphics engine. // Export stuff to the graphics engine.
set_visible_world_and_actor(master_.get(), admin_id_); set_visible_world_and_actor(master_.get(), admin_id_);
// for ticking. // Trigger the loading of the lua source.
next_tick_ = 0.0; rescan_lua_source();
} }
virtual void do_syntax_error(std::string_view error) override { virtual void do_syntax_error(std::string_view error) override {
@@ -91,35 +86,7 @@ public:
} }
virtual void do_work_command() override { virtual void do_work_command() override {
master_->snapshot(); do_unknown_command("work");
StreamBuffer datapack;
StreamBuffer retvals;
datapack.write_string("engio");
datapack.write_string("myfunction");
datapack.write_simple_dynamic_tag(SimpleDynamicTag::NUMBER);
datapack.write_double(3.7);
datapack.write_simple_dynamic_tag(SimpleDynamicTag::STRING);
datapack.write_string("banana");
datapack.write_simple_dynamic_tag(SimpleDynamicTag::BOOLEAN);
datapack.write_bool(true);
master_->probe_lua_call(admin_id_, admin_id_, datapack.view(), &retvals);
while (!retvals.empty()) {
SimpleDynamicValue value;
retvals.read_simple_dynamic(&value);
if (value.type == SimpleDynamicTag::NUMBER) {
util::dprint("retval: ", value.x);
} else if (value.type == SimpleDynamicTag::STRING) {
util::dprint("retval: ", value.s);
} else if (value.type == SimpleDynamicTag::BOOLEAN) {
util::dprint(value.x ? "retval: true" : "retval: false");
} else if (value.type == SimpleDynamicTag::VECTOR) {
util::dprint("retval: ", value.x, " ", value.y, " ", value.z);
} else {
util::dprint("retval: invalid");
break;
}
}
master_->rollback();
} }
virtual void do_display_command() override { virtual void do_display_command() override {
@@ -194,7 +161,16 @@ public:
} }
virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override { virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override {
if (place_id == 0) place_id = admin_id_;
switch (kind) { switch (kind) {
case AccessKind::INVOKE_LUA_CALL:
case AccessKind::INVOKE_LUA_EXPR:
case AccessKind::INVOKE_FLUSH_PRINTS:
case AccessKind::INVOKE_TICK:
case AccessKind::INVOKE_LUA_SOURCE: {
delayed_invocations_.emplace_back(kind, admin_id_, place_id, datapk);
break;
}
case AccessKind::PROBE_LUA_CALL: { case AccessKind::PROBE_LUA_CALL: {
master_->snapshot(); master_->snapshot();
master_->probe_lua_call(admin_id_, place_id, datapk, retpk); master_->probe_lua_call(admin_id_, place_id, datapk, retpk);
@@ -202,7 +178,7 @@ public:
break; break;
} }
default: { default: {
delayed_invocations_.emplace_back(kind, admin_id_, place_id, datapk); stdostream() << "Invalid event_access: " << int(kind) << std::endl;
} }
} }
} }

View File

@@ -546,44 +546,49 @@ void World::probe_lua_call(int64_t actor_id, int64_t place_id, std::string_view
// some lua source file tries to modify, say, tangible state // some lua source file tries to modify, say, tangible state
// in top-level code. // in top-level code.
// //
void World::rebuild_sourcedb() { bool World::rebuild_sourcedb() {
bool ok = true;
for (const eng::string &mod: source_db_.modules()) { for (const eng::string &mod: source_db_.modules()) {
open_lthread_state(0, 0, 0, false, true); open_lthread_state(0, 0, 0, false, true);
eng::string err = source_db_.rebuild_module(mod); eng::string err = source_db_.rebuild_module(mod);
eng::string prints = lthread_prints_->str(); eng::string prints = lthread_prints_->str();
lthread_prints_.reset(); lthread_prints_.reset();
close_lthread_state(); close_lthread_state();
if (!err.empty()) ok = false;
if (!err.empty() || !prints.empty()) { if (!err.empty() || !prints.empty()) {
util::dprint("Loading Module ", mod); util::dprint("Loading Module ", mod);
if (!err.empty()) util::dprint(err); if (!err.empty()) util::dprint(err);
if (!prints.empty()) util::dprint(prints); if (!prints.empty()) util::dprint(prints);
} }
} }
return ok;
} }
void World::update_source(const util::LuaSourceVec &source) { bool World::update_source(const util::LuaSourceVec &source) {
assert(stack_is_clear()); assert(stack_is_clear());
source_db_.update(source); source_db_.update(source);
rebuild_sourcedb(); return rebuild_sourcedb();
assert(stack_is_clear()); assert(stack_is_clear());
} }
void World::update_source(const util::LuaSourcePtr &source) { bool World::update_source(const util::LuaSourcePtr &source) {
if (source != nullptr) { if (source == nullptr) {
update_source(*source); return false;
} }
return update_source(*source);
} }
void World::update_source(std::string_view sourcepack) { bool World::update_source(std::string_view sourcepack) {
if (!sourcepack.empty()) { if (sourcepack.empty()) {
return false;
}
try { try {
StreamBuffer sb(sourcepack); StreamBuffer sb(sourcepack);
util::LuaSourceVec sv; util::LuaSourceVec sv;
SourceDB::deserialize_source(&sv, &sb); SourceDB::deserialize_source(&sv, &sb);
update_source(sv); return update_source(sv);
} catch (const StreamException &ex) { } catch (const StreamException &ex) {
return; return false;
}
} }
} }
@@ -960,11 +965,40 @@ void World::invoke_tick(int64_t actor_id, int64_t place_id, std::string_view dat
} }
void World::invoke_lua_source(int64_t actor_id, int64_t place_id, std::string_view datapack) { void World::invoke_lua_source(int64_t actor_id, int64_t place_id, std::string_view datapack) {
if (!is_authoritative()) { // Sanity check arguments.
return; // We also need some kind of authentication here.
if (!is_authoritative()) return;
if (actor_id != place_id) return;
// Check if this is the first time we're loading the source.
bool brand_new = (source_db_.modules().size() == 1);
// Compile and load the source.
bool success = update_source(datapack);
// Call world.init
if (brand_new) {
if (success) {
{
lua_State *L = state();
LuaVar lclass, lfunc;
LuaExtStack LS(L, lclass, lfunc);
LS.getclass(lclass, "world");
if (LS.classname(lclass) != "") {
LS.rawget(lfunc, lclass, "init");
spawn(LS, actor_id, place_id, lfunc, 0, false);
} }
// We need some kind of authentication here. }
update_source(datapack); run_scheduled_threads();
} else {
util::dprint("Did not run world.init because of lua errors.");
util::dprint("You will need to fix the errors then run it manually.");
}
}
// Run the new thread and return.
assert(stack_is_clear());
} }
void World::guard_blockable(lua_State *L, const char *fn) { void World::guard_blockable(lua_State *L, const char *fn) {

View File

@@ -216,11 +216,11 @@ public:
// This is used to create a temporary actor which is used during // This is used to create a temporary actor which is used during
// the login process. // the login process.
// //
// If this is a master model, The function 'login.initialize' // If this is a master model, The function 'login.init'
// called. Then, the following login flags are set: // called. Then, the following login flags are set:
// can_be_controlled, is_controlled, and delete_on_disconnect. // can_be_controlled, is_controlled, and delete_on_disconnect.
// //
// In a client model, 'login.initialize' is not called, // In a client model, 'login.init' is not called,
// and the login flags are not used in client models. // and the login flags are not used in client models.
// //
int64_t create_login_actor(); int64_t create_login_actor();
@@ -265,19 +265,21 @@ public:
// //
const PrintBuffer *get_printbuffer(int64_t actor_id); const PrintBuffer *get_printbuffer(int64_t actor_id);
// Update the source database from disk. // Rebuild the global environment from the source database.
//
// Returns true if the rebuild goes without errors.
//
bool rebuild_sourcedb();
// Update the source database from disk, then rebuild the global environment.
// //
// Special case: if the source pointer is nullptr, does not update. // Special case: if the source pointer is nullptr, does not update.
// The final form takes a sourcepk, a serialized representation
// of a LuaSourceVec.
// //
void update_source(const util::LuaSourceVec &source); // Returns true if the update goes without errors.
void update_source(const util::LuaSourcePtr &source);
void update_source(std::string_view sourcepk);
// Rebuild the source database.
// //
void rebuild_sourcedb(); bool update_source(const util::LuaSourceVec &source);
bool update_source(const util::LuaSourcePtr &source);
bool update_source(std::string_view sourcepk);
// Supply an HTTP response to an outstanding HTTP request. // Supply an HTTP response to an outstanding HTTP request.
// //

View File

@@ -646,14 +646,8 @@ class Driver {
ssl_load_certificate_authorities(ssl_client_secure_ctx_.get()); ssl_load_certificate_authorities(ssl_client_secure_ctx_.get());
sslutil::ctx_load_dummy_cert(ssl_server_ctx_.get()); sslutil::ctx_load_dummy_cert(ssl_server_ctx_.get());
// Read the initial lua source code.
drvutil::ostringstream srcpak;
std::string srcpakerr = drvutil::package_lua_source(luprexroot, &srcpak);
if_error_print_and_exit(srcpakerr);
// Initialize the engine. // Initialize the engine.
std::string_view srcpakv = srcpak.view(); engw.play_initialize(&engw, argv[0], replaylogfn.c_str());
engw.play_initialize(&engw, argc, argv, srcpakv.size(), srcpakv.data(), replaylogfn.c_str());
if_error_print_and_exit(engw.error); if_error_print_and_exit(engw.error);
// Set up listening ports. // Set up listening ports.

View File

@@ -1,14 +1,23 @@
makeclass("world")
makeclass('login') makeclass('login')
makeclass("engio") makeclass("engio")
makeclass('cube') makeclass('cube')
makeclass('sphere') makeclass('sphere')
-- This gets called on every login except the admin user.
function login.init() function login.init()
local actor = tangible.actor() local actor = tangible.actor()
dprint("login.init:", actor) dprint("login.init:", actor)
tangible.animinit(actor, {bp="character", plane="earth", xyz={0, 0, 90}}) tangible.animinit(actor, {bp="character", plane="earth", xyz={0, 0, 90}})
tangible.build{class=cube, xyz={500,-100,0}} end
tangible.build{class=sphere, xyz={500,100,0}}
-- This gets called on the admin user. You can call login.init in here if you want.
function world.init()
local actor = tangible.actor()
dprint("world.init:", actor)
tangible.build{class=cube, plane="earth", xyz={500,-100,0}}
tangible.build{class=sphere, plane="earth", xyz={500,100,0}}
login.init()
end end
function engio.move(action, xyz, facing) function engio.move(action, xyz, facing)