mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-09-25 17:40:19 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9af3b60d4d | ||
![]() |
d5fc36f877 | ||
aa3dacc84b | |||
a29de394ee | |||
ca299e0a5b | |||
794b881c4d | |||
e8659962a5 | |||
2090062c75 | |||
b25b229eb2 | |||
c2853d9271 | |||
cb85f7b7c9 | |||
80f0ff7479 | |||
2aa23a83de | |||
43aa4eaeb8 |
@@ -44,6 +44,7 @@ add_executable(openfusion ${SOURCES})
|
||||
set_target_properties(openfusion PROPERTIES OUTPUT_NAME ${BIN_NAME})
|
||||
|
||||
target_link_libraries(openfusion sqlite3)
|
||||
target_link_libraries(openfusion lua51)
|
||||
|
||||
# Makes it so config, tdata, etc. get picked up when starting via the debugger in VS
|
||||
set_property(TARGET openfusion PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
|
23
Makefile
23
Makefile
@@ -5,8 +5,10 @@ CXX=clang++
|
||||
# -w suppresses all warnings (the part that's commented out helps me find memory leaks, it ruins performance though!)
|
||||
# If compiling with ASAN, invoke like this: $ LSAN_OPTIONS=suppressions=suppr.txt bin/fusion
|
||||
CFLAGS=-O3 #-g3 -fsanitize=address
|
||||
CXXFLAGS=-Wall -Wno-unknown-pragmas -std=c++17 -O2 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) -DGIT_VERSION=\"$(GIT_VERSION)\" -I./src -I./vendor #-g3 -fsanitize=address
|
||||
LDFLAGS=-lpthread -lsqlite3 #-g3 -fsanitize=address
|
||||
LCFLAGS=`pkg-config --cflags luajit`
|
||||
LLIBFLAGS=`pkg-config --libs luajit`
|
||||
CXXFLAGS=-Wall -Wno-unknown-pragmas -std=c++17 -O2 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) -DGIT_VERSION=\"$(GIT_VERSION)\" -I./src -I./vendor $(LCFLAGS) #-g3 -fsanitize=address
|
||||
LDFLAGS=-lpthread -lsqlite3 $(LLIBFLAGS) -lstdc++fs #-g3 -fsanitize=address
|
||||
# specifies the name of our exectuable
|
||||
SERVER=bin/fusion
|
||||
|
||||
@@ -18,8 +20,8 @@ PROTOCOL_VERSION?=104
|
||||
WIN_CC=x86_64-w64-mingw32-gcc
|
||||
WIN_CXX=x86_64-w64-mingw32-g++
|
||||
WIN_CFLAGS=-O3 #-g3 -fsanitize=address
|
||||
WIN_CXXFLAGS=-D_WIN32_WINNT=0x0601 -Wall -Wno-unknown-pragmas -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) -DGIT_VERSION=\"$(GIT_VERSION)\" -I./src -I./vendor #-g3 -fsanitize=address
|
||||
WIN_LDFLAGS=-static -lws2_32 -lwsock32 -lsqlite3 #-g3 -fsanitize=address
|
||||
WIN_CXXFLAGS=-D_WIN32_WINNT=0x0601 -Wall -Wno-unknown-pragmas -std=c++17 -O3 -DPROTOCOL_VERSION=$(PROTOCOL_VERSION) -DGIT_VERSION=\"$(GIT_VERSION)\" -I./src -I./vendor $(LCFLAGS) #-g3 -fsanitize=address
|
||||
WIN_LDFLAGS=-static -lws2_32 -lwsock32 -lsqlite3 $(LLIBFLAGS) -lstdc++fs #-g3 -fsanitize=address
|
||||
WIN_SERVER=bin/winfusion.exe
|
||||
|
||||
# C code; currently exclusively from vendored libraries
|
||||
@@ -43,6 +45,12 @@ CXXSRC=\
|
||||
src/servers/CNLoginServer.cpp\
|
||||
src/servers/CNShardServer.cpp\
|
||||
src/servers/Monitor.cpp\
|
||||
src/lua/LuaManager.cpp\
|
||||
src/lua/EventWrapper.cpp\
|
||||
src/lua/WorldWrapper.cpp\
|
||||
src/lua/EntityWrapper.cpp\
|
||||
src/lua/PlayerWrapper.cpp\
|
||||
src/lua/NPCWrapper.cpp\
|
||||
src/db/init.cpp\
|
||||
src/db/login.cpp\
|
||||
src/db/shard.cpp\
|
||||
@@ -86,6 +94,13 @@ CXXHDR=\
|
||||
src/servers/CNLoginServer.hpp\
|
||||
src/servers/CNShardServer.hpp\
|
||||
src/servers/Monitor.hpp\
|
||||
src/lua/LuaManager.hpp\
|
||||
src/lua/LuaWrapper.hpp\
|
||||
src/lua/EventWrapper.hpp\
|
||||
src/lua/WorldWrapper.hpp\
|
||||
src/lua/EntityWrapper.hpp\
|
||||
src/lua/PlayerWrapper.hpp\
|
||||
src/lua/NPCWrapper.hpp\
|
||||
src/db/Database.hpp\
|
||||
src/db/internal.hpp\
|
||||
vendor/bcrypt/BCrypt.hpp\
|
||||
|
@@ -18,6 +18,8 @@ for:
|
||||
matrix:
|
||||
only:
|
||||
- image: GCP-Linux-Ubuntu2004
|
||||
install:
|
||||
- sh: sudo apt install libluajit-5.1-dev -y
|
||||
build_script:
|
||||
- ps: |
|
||||
$versions = "104", "728", "1013"
|
||||
@@ -48,6 +50,7 @@ for:
|
||||
- image: GCP-Windows-VS2019
|
||||
install:
|
||||
- cmd: vcpkg install sqlite3:x64-windows
|
||||
- cmd: vcpkg install luajit:x64-windows
|
||||
- cmd: vcpkg integrate install
|
||||
build_script:
|
||||
- ps: |
|
||||
|
28
scripts/test.lua
Normal file
28
scripts/test.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
print("Hello wtf!")
|
||||
|
||||
function onJoin(plr)
|
||||
print(plr.type .. " " .. plr.name .. " joined from LUA!!")
|
||||
plr.onChat:listen(function(msg)
|
||||
print(plr.name .. " said : \'" .. msg .. "\'")
|
||||
|
||||
if msg == "kickme" then
|
||||
plr:kick()
|
||||
elseif msg == "hi" then
|
||||
print("hello " .. plr.name)
|
||||
elseif msg == "pet" then
|
||||
local dog = NPC.new(plr.x, plr.y, plr.z, 3054)
|
||||
while wait(2) and plr:exists() do
|
||||
dog:moveTo(plr.x + math.random(-500, 500), plr.y + math.random(-500, 500), plr.z)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
World.onPlayerAdded:listen(onJoin)
|
||||
|
||||
for i, plr in ipairs(World.players) do
|
||||
onJoin(plr)
|
||||
end
|
||||
|
||||
wait(2)
|
||||
print("Hello world ~2 seconds later! running protcol version " .. World.version)
|
@@ -19,6 +19,10 @@ static void chatHandler(CNSocket* sock, CNPacketData* data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the player has an onChat Lua event registered, call it
|
||||
if (plr->onChat != nullptr)
|
||||
plr->onChat->call(fullChat.c_str());
|
||||
|
||||
if (plr->iSpecialState & CN_SPECIAL_STATE_FLAG__MUTE_FREECHAT)
|
||||
return;
|
||||
|
||||
|
@@ -10,6 +10,8 @@
|
||||
#include "Transport.hpp"
|
||||
#include "Missions.hpp"
|
||||
|
||||
#include "lua/LuaManager.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include <math.h>
|
||||
@@ -1190,6 +1192,12 @@ static void pathCommand(std::string full, std::vector<std::string>& args, CNSock
|
||||
Chat::sendServerMessage(sock, "[PATH] Unknown argument '" + args[1] + "'");
|
||||
}
|
||||
|
||||
static void reloadScriptsCommand(std::string full, std::vector<std::string>& args, CNSocket *sock) {
|
||||
// reloads all scripts
|
||||
LuaManager::stopScripts();
|
||||
LuaManager::loadScripts();
|
||||
}
|
||||
|
||||
static void registerCommand(std::string cmd, int requiredLevel, CommandHandler handlr, std::string help) {
|
||||
commands[cmd] = ChatCommand(requiredLevel, handlr, help);
|
||||
}
|
||||
@@ -1229,4 +1237,5 @@ void CustomCommands::init() {
|
||||
registerCommand("unregisterall", 50, unregisterallCommand, "clear all SCAMPER and MSS destinations");
|
||||
registerCommand("redeem", 100, redeemCommand, "redeem a code item");
|
||||
registerCommand("path", 30, pathCommand, "edit NPC paths");
|
||||
registerCommand("rscripts", 30, reloadScriptsCommand, "stops all script states and reloads all scripts");
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
#include "Chunking.hpp"
|
||||
#include "Entities.hpp"
|
||||
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
|
||||
#define ACTIVE_MISSION_COUNT 6
|
||||
|
||||
#define PC_MAXHEALTH(level) (925 + 75 * (level))
|
||||
@@ -87,7 +89,15 @@ struct Player : public Entity {
|
||||
time_t lastShot = 0;
|
||||
std::vector<sItemBase> buyback = {};
|
||||
|
||||
// lua events
|
||||
lEvent *onChat = nullptr;
|
||||
|
||||
Player() { type = EntityType::PLAYER; }
|
||||
~Player() {
|
||||
// if an event was registered, free it
|
||||
if (onChat != nullptr)
|
||||
delete onChat;
|
||||
}
|
||||
|
||||
virtual void enterIntoViewOf(CNSocket *sock) override;
|
||||
virtual void disappearFromViewOf(CNSocket *sock) override;
|
||||
|
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "settings.hpp"
|
||||
|
||||
#include "lua/LuaManager.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -40,12 +42,18 @@ static void addPlayer(CNSocket* key, Player plr) {
|
||||
|
||||
std::cout << getPlayerName(p) << " has joined!" << std::endl;
|
||||
std::cout << players.size() << " players" << std::endl;
|
||||
|
||||
// call events
|
||||
LuaManager::playerAdded(key);
|
||||
}
|
||||
|
||||
void PlayerManager::removePlayer(CNSocket* key) {
|
||||
Player* plr = getPlayer(key);
|
||||
uint64_t fromInstance = plr->instanceID;
|
||||
|
||||
// call events
|
||||
LuaManager::playerRemoved(key);
|
||||
|
||||
Groups::groupKickPlayer(plr);
|
||||
|
||||
// remove player's bullets
|
||||
|
174
src/lua/EntityWrapper.cpp
Normal file
174
src/lua/EntityWrapper.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/EntityWrapper.hpp"
|
||||
|
||||
#include "Entities.hpp"
|
||||
|
||||
#define LIBNAME "Entity"
|
||||
#define SUPERTBL "__entSUPERCLASSES"
|
||||
|
||||
#define ENTYGONESTR "Entity doesn't exist anymore!"
|
||||
|
||||
EntityRef* grabBaseEntityRef(lua_State *state, int indx) {
|
||||
// first, make sure its a userdata
|
||||
luaL_checktype(state, indx, LUA_TUSERDATA);
|
||||
|
||||
// grab the super class table
|
||||
lua_getfield(state, LUA_REGISTRYINDEX, SUPERTBL);
|
||||
|
||||
// grab the userdata
|
||||
EntityRef *data = (EntityRef*)lua_touserdata(state, indx);
|
||||
|
||||
// check if it doesn't have a metatable
|
||||
if (!lua_getmetatable(state, indx)) {
|
||||
luaL_typerror(state, indx, LIBNAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// index the super class table
|
||||
lua_gettable(state, -2);
|
||||
|
||||
// if the index was nil, it doesn't exist
|
||||
if (lua_isnil(state, -1)) {
|
||||
lua_pop(state, 1);
|
||||
luaL_typerror(state, indx, LIBNAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// it's good :)
|
||||
lua_pop(state, 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
// check at index
|
||||
EntityRef* grabEntityRef(lua_State *state, int indx) {
|
||||
EntityRef *ref = grabBaseEntityRef(state, indx);
|
||||
|
||||
if (ref == NULL || !ref->isValid())
|
||||
return NULL;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
// =============================================== [[ GETTERS ]] ===============================================
|
||||
|
||||
static int ent_getX(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushnumber(state, ref->getEntity()->x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ent_getY(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushnumber(state, ref->getEntity()->y);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ent_getZ(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushnumber(state, ref->getEntity()->z);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ent_getType(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
// push the type string
|
||||
switch(ref->type) {
|
||||
case EntityType::PLAYER:
|
||||
lua_pushstring(state, "Player");
|
||||
break;
|
||||
case EntityType::MOB:
|
||||
lua_pushstring(state, "Mob");
|
||||
break;
|
||||
case EntityType::EGG:
|
||||
lua_pushstring(state, "Egg");
|
||||
break;
|
||||
case EntityType::BUS:
|
||||
lua_pushstring(state, "Bus");
|
||||
break;
|
||||
default: // INVALID, COMBAT_NPC, SIMPLE_NPC
|
||||
lua_pushstring(state, "Entity");
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static luaL_Reg ent_getters[] = {
|
||||
{"x", ent_getX},
|
||||
{"y", ent_getY},
|
||||
{"z", ent_getZ},
|
||||
{"type", ent_getType},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// =============================================== [[ METHODS ]] ===============================================
|
||||
|
||||
static int ent_exists(lua_State *state) {
|
||||
EntityRef *data = (EntityRef*)grabBaseEntityRef(state, 1);
|
||||
|
||||
lua_pushboolean(state, !(data == NULL || !data->isValid()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static luaL_Reg ent_methods[] = {
|
||||
{"exists", ent_exists},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
void LuaManager::Entity::init(lua_State *state) {
|
||||
// register our library as a global (and leave it on the stack)
|
||||
luaL_register(state, LIBNAME, ent_methods);
|
||||
lua_pop(state, 1); // pop library table
|
||||
|
||||
// will hold our super classes
|
||||
lua_pushstring(state, SUPERTBL);
|
||||
lua_newtable(state);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
void LuaManager::Entity::registerSuper(lua_State *state, const char *tname) {
|
||||
// grab the super class table
|
||||
lua_getfield(state, LUA_REGISTRYINDEX, SUPERTBL);
|
||||
|
||||
// grab the metatable
|
||||
lua_getfield(state, LUA_REGISTRYINDEX, tname);
|
||||
lua_pushboolean(state, 1);
|
||||
|
||||
// finally, set the index
|
||||
lua_rawset(state, -3);
|
||||
lua_pop(state, 1); // pop the super class table
|
||||
}
|
||||
|
||||
void LuaManager::Entity::addGetters(lua_State *state) {
|
||||
luaL_register(state, NULL, ent_getters);
|
||||
}
|
||||
|
||||
void LuaManager::Entity::addMethods(lua_State *state) {
|
||||
luaL_register(state, NULL, ent_methods);
|
||||
}
|
||||
|
||||
void LuaManager::Entity::push(lua_State *state, EntityRef ref, const char *tname) {
|
||||
// creates the udata and copies the reference to the udata
|
||||
EntityRef *ent = (EntityRef*)lua_newuserdata(state, sizeof(EntityRef));
|
||||
*ent = ref;
|
||||
|
||||
// attaches our metatable from the registry to the udata
|
||||
luaL_getmetatable(state, tname);
|
||||
lua_setmetatable(state, -2);
|
||||
}
|
19
src/lua/EntityWrapper.hpp
Normal file
19
src/lua/EntityWrapper.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/LuaManager.hpp"
|
||||
|
||||
#include "Entities.hpp"
|
||||
|
||||
namespace LuaManager {
|
||||
namespace Entity {
|
||||
void init(lua_State *state);
|
||||
|
||||
void registerSuper(lua_State *state, const char *tname);
|
||||
|
||||
void addGetters(lua_State *state);
|
||||
void addMethods(lua_State *state);
|
||||
|
||||
void push(lua_State *state, EntityRef ref, const char *tname);
|
||||
}
|
||||
}
|
190
src/lua/EventWrapper.cpp
Normal file
190
src/lua/EventWrapper.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
This loads the Event library, basically a wrapper for lEvents, allows the lua script to register callbacks or wait for an event to be fired
|
||||
*/
|
||||
|
||||
#include "lua/EventWrapper.hpp"
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#define LIBNAME "Event"
|
||||
#define LISTNR "Listener"
|
||||
|
||||
typedef lEvent* eventData;
|
||||
|
||||
std::unordered_set<lEvent*> activeEvents;
|
||||
|
||||
struct lstnrData {
|
||||
lEvent *event;
|
||||
uint32_t rawListener;
|
||||
};
|
||||
|
||||
static void pushListener(lua_State *state, lEvent *event, uint32_t listener) {
|
||||
lstnrData *lstnr = (lstnrData*)lua_newuserdata(state, sizeof(lstnrData));
|
||||
lstnr->event = event;
|
||||
lstnr->rawListener = listener;
|
||||
|
||||
// attaches our metatable from the registry to the udata
|
||||
luaL_getmetatable(state, LISTNR);
|
||||
lua_setmetatable(state, -2);
|
||||
}
|
||||
|
||||
static void pushEvent(lua_State *state, lEvent *event) {
|
||||
eventData *eData = (eventData*)lua_newuserdata(state, sizeof(eventData));
|
||||
*eData = event;
|
||||
|
||||
// attaches our metatable from the registry to the udata
|
||||
luaL_getmetatable(state, LIBNAME);
|
||||
lua_setmetatable(state, -2);
|
||||
}
|
||||
|
||||
static lEvent *grabEvent(lua_State *state, int indx) {
|
||||
// first, make sure its a userdata
|
||||
luaL_checktype(state, indx, LUA_TUSERDATA);
|
||||
|
||||
// now, check and make sure its our libraries metatable attached to this userdata
|
||||
eventData *event = (eventData*)luaL_checkudata(state, indx, LIBNAME);
|
||||
if (event == NULL) {
|
||||
luaL_typerror(state, indx, LIBNAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if the event is not active return NULL and don't throw an error (this is an easily forgivable user-error)
|
||||
if (activeEvents.find(*event) == activeEvents.end())
|
||||
return NULL;
|
||||
|
||||
// return the pointer to the lEvent
|
||||
return *event;
|
||||
}
|
||||
|
||||
static lstnrData *grabListener(lua_State *state, int indx) {
|
||||
// first, make sure its a userdata
|
||||
luaL_checktype(state, indx, LUA_TUSERDATA);
|
||||
|
||||
// now, check and make sure its our libraries metatable attached to this userdata
|
||||
lstnrData *lstnr = (lstnrData*)luaL_checkudata(state, indx, LISTNR);
|
||||
if (lstnr == NULL) {
|
||||
luaL_typerror(state, indx, LISTNR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// if the event is not active return NULL and don't throw an error (this is an easily forgivable user-error)
|
||||
if (activeEvents.find(lstnr->event) == activeEvents.end())
|
||||
return NULL;
|
||||
|
||||
// return the pointer to the lEvent
|
||||
return lstnr;
|
||||
}
|
||||
|
||||
// connects a callback to an event
|
||||
static int evnt_listen(lua_State *state) {
|
||||
int nargs = lua_gettop(state);
|
||||
lEvent *event = grabEvent(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (event == NULL)
|
||||
return 0;
|
||||
|
||||
// for each argument passed, check that it's a function and add it to the event
|
||||
for (int i = 2; i <= nargs; i++) {
|
||||
luaL_checktype(state, i, LUA_TFUNCTION);
|
||||
lua_pushvalue(state, i);
|
||||
pushListener(state, event, event->addCallback(state, luaL_ref(state, LUA_REGISTRYINDEX)));
|
||||
}
|
||||
|
||||
// we return this many listeners
|
||||
return nargs - 1;
|
||||
}
|
||||
|
||||
// yields the thread until the event is triggered
|
||||
static int evnt_wait(lua_State *state) {
|
||||
lEvent *event = grabEvent(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (event == NULL)
|
||||
return 0;
|
||||
|
||||
event->addWait(state);
|
||||
return lua_yield(state, 0);
|
||||
}
|
||||
|
||||
static int lstnr_disconnect(lua_State *state) {
|
||||
lstnrData *lstnr = grabListener(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (lstnr == NULL)
|
||||
return 0;
|
||||
|
||||
// disconnect the event
|
||||
lstnr->event->disconnectEvent(lstnr->rawListener);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lstnr_reconnect(lua_State *state) {
|
||||
lstnrData *lstnr = grabListener(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (lstnr == NULL)
|
||||
return 0;
|
||||
|
||||
// disconnect the event
|
||||
lstnr->event->reconnectEvent(lstnr->rawListener);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lstnr_gc(lua_State *state) {
|
||||
lstnrData *lstnr = grabListener(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (lstnr == NULL)
|
||||
return 0;
|
||||
|
||||
// if the listener is disabled, clear it
|
||||
if (lstnr->event->isDisabled(lstnr->rawListener))
|
||||
lstnr->event->clear(lstnr->rawListener);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static luaL_Reg evnt_methods[] = {
|
||||
{"listen", evnt_listen},
|
||||
{"wait", evnt_wait},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static luaL_Reg lstnr_methods[] = {
|
||||
{"disconnect", lstnr_disconnect},
|
||||
{"reconnect", lstnr_reconnect},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
void LuaManager::Event::init(lua_State *state) {
|
||||
// register the library & pop it
|
||||
luaL_register(state, LIBNAME, evnt_methods);
|
||||
|
||||
// create the event metatable
|
||||
luaL_newmetatable(state, LIBNAME);
|
||||
lua_pushstring(state, "__index");
|
||||
lua_pushvalue(state, -3);
|
||||
lua_rawset(state, -3);
|
||||
|
||||
// create the listener metatable
|
||||
luaL_newmetatable(state, LISTNR);
|
||||
lua_pushstring(state, "__index");
|
||||
lua_newtable(state);
|
||||
luaL_register(state, NULL, lstnr_methods);
|
||||
lua_rawset(state, -3);
|
||||
lua_pushstring(state, "__gc");
|
||||
lua_pushcfunction(state, lstnr_gc);
|
||||
lua_rawset(state, -3);
|
||||
|
||||
// pop the tables off the stack
|
||||
lua_pop(state, 3);
|
||||
|
||||
activeEvents = std::unordered_set<lEvent*>();
|
||||
}
|
||||
|
||||
// just a wrapper for pushEvent()
|
||||
void LuaManager::Event::push(lua_State *state, lEvent *event) {
|
||||
pushEvent(state, event);
|
||||
}
|
10
src/lua/EventWrapper.hpp
Normal file
10
src/lua/EventWrapper.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
|
||||
namespace LuaManager {
|
||||
namespace Event {
|
||||
void init(lua_State *state);
|
||||
|
||||
void push(lua_State *state, lEvent *event);
|
||||
}
|
||||
}
|
185
src/lua/LuaManager.cpp
Normal file
185
src/lua/LuaManager.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/EventWrapper.hpp"
|
||||
#include "lua/WorldWrapper.hpp"
|
||||
#include "lua/PlayerWrapper.hpp"
|
||||
#include "lua/NPCWrapper.hpp"
|
||||
|
||||
#include "PlayerManager.hpp"
|
||||
#include "servers/CNShardServer.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
time_t getTime();
|
||||
class Script;
|
||||
|
||||
// our "main" state, holds our environment
|
||||
lua_State *LuaManager::global;
|
||||
std::map<lua_State*, Script*> activeScripts;
|
||||
|
||||
/*
|
||||
Basically each script is treated as a coroutine, when wait() is called it gets yielded and is pushed onto the scheduler queue to be resumed.
|
||||
*/
|
||||
class Script {
|
||||
private:
|
||||
lua_State *thread;
|
||||
lRegistry threadRef; // we'll need to unref this when closing this state
|
||||
|
||||
public:
|
||||
Script(std::string source) {
|
||||
// make the thread & register it in the registry
|
||||
thread = lua_newthread(LuaManager::global);
|
||||
threadRef = luaL_ref(LuaManager::global, LUA_REGISTRYINDEX);
|
||||
|
||||
// add this script to the map
|
||||
activeScripts[thread] = this;
|
||||
|
||||
// compile & run the script, if it error'd, print the error
|
||||
int _retCode;
|
||||
if (luaL_loadfile(thread, source.c_str()) || ((_retCode = lua_resume(thread, 0)) != 0 && (_retCode != LUA_YIELD))) {
|
||||
std::cout << "[LUA ERROR]: " << lua_tostring(thread, -1) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// unregister all of our events from the wrappers
|
||||
~Script() {
|
||||
LuaManager::clearState(thread);
|
||||
|
||||
// remove it from the global registry
|
||||
luaL_unref(LuaManager::global, LUA_REGISTRYINDEX, threadRef);
|
||||
}
|
||||
|
||||
// c++ moment....
|
||||
lua_State* getState() {
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
|
||||
struct scheduledThread {
|
||||
lRegistry ref; // ref that should be unref'd before resuming
|
||||
time_t time;
|
||||
};
|
||||
std::map<lua_State*, scheduledThread> scheduleQueue;
|
||||
|
||||
// pauses the script for x seconds, not very accurate but should be
|
||||
// called within ~60ms or less of when it was schedueled
|
||||
// will also return the time the thread was paused
|
||||
int OF_wait(lua_State *state) {
|
||||
double seconds = luaL_checknumber(state, 1);
|
||||
|
||||
// register the thread in the global registry so we don't get GC'd
|
||||
lua_pushthread(state);
|
||||
lRegistry ref = luaL_ref(state, LUA_REGISTRYINDEX); // threads decentant of a global state all share the global registry
|
||||
|
||||
// yield the state and push the state onto our scheduler queue
|
||||
scheduleQueue[state] = {ref, (int)(seconds*1000) + getTime()};
|
||||
return lua_yield(state, 0);
|
||||
}
|
||||
|
||||
void luaScheduler(CNServer *serv, time_t currtime) {
|
||||
for (auto iter = scheduleQueue.begin(); iter != scheduleQueue.end();) {
|
||||
time_t event = (*iter).second.time;
|
||||
lRegistry ref = (*iter).second.ref;
|
||||
lua_State *thread = (*iter).first;
|
||||
// is it time to run the event?
|
||||
if (event <= currtime) {
|
||||
// remove from the scheduler queue
|
||||
scheduleQueue.erase(iter++);
|
||||
|
||||
// unregister the thread
|
||||
luaL_unref(thread, LUA_REGISTRYINDEX, ref);
|
||||
|
||||
// resume the state, (wait() returns the delta time since call)
|
||||
lua_pushnumber(thread, ((double)currtime - event)/10);
|
||||
yieldCall(thread, 1);
|
||||
} else // go to the next iteration
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::printError(std::string err) {
|
||||
std::cerr << "[LUA ERR]: " << err << std::endl;
|
||||
}
|
||||
|
||||
void LuaManager::init() {
|
||||
// allocate our state
|
||||
global = luaL_newstate();
|
||||
|
||||
// open lua's base libraries (excluding the IO for now)
|
||||
luaopen_base(global);
|
||||
luaopen_table(global);
|
||||
luaopen_string(global);
|
||||
luaopen_math(global);
|
||||
luaopen_debug(global);
|
||||
|
||||
// add wait()
|
||||
lua_register(global, "wait", OF_wait);
|
||||
|
||||
// register our libraries
|
||||
Event::init(global);
|
||||
World::init(global);
|
||||
Entity::init(global);
|
||||
Player::init(global);
|
||||
NPC::init(global);
|
||||
|
||||
activeScripts = std::map<lua_State*, Script*>();
|
||||
|
||||
// we want to be called after every poll(), so our timer delta is set to 0
|
||||
REGISTER_SHARD_TIMER(luaScheduler, 0);
|
||||
|
||||
// load our scripts
|
||||
loadScripts();
|
||||
}
|
||||
|
||||
void LuaManager::runScript(std::string filename) {
|
||||
new Script(filename);
|
||||
}
|
||||
|
||||
void LuaManager::stopScripts() {
|
||||
// clear the scheduler queue
|
||||
scheduleQueue.clear();
|
||||
|
||||
// free all the scripts, they'll take care of everything for us :)
|
||||
for (auto as : activeScripts) {
|
||||
delete as.second;
|
||||
}
|
||||
|
||||
// finally clear the map
|
||||
activeScripts.clear();
|
||||
|
||||
// walk through each player and unregister each event
|
||||
for (auto pair: PlayerManager::players) {
|
||||
if (pair.second->onChat != nullptr) {
|
||||
delete pair.second->onChat;
|
||||
pair.second->onChat = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::loadScripts() {
|
||||
if (!std::filesystem::exists(settings::SCRIPTSDIR)) {
|
||||
std::cout << "[WARN] scripts directory \"" << settings::SCRIPTSDIR << "\" doesn't exist!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// for each file in the scripts director, load the script
|
||||
std::filesystem::path dir(settings::SCRIPTSDIR);
|
||||
for (auto &d : std::filesystem::directory_iterator(dir)) {
|
||||
if (d.path().extension().u8string() == ".lua")
|
||||
runScript(d.path().u8string());
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::clearState(lua_State *state) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void LuaManager::playerAdded(CNSocket *sock) {
|
||||
World::playerAdded(sock);
|
||||
}
|
||||
|
||||
void LuaManager::playerRemoved(CNSocket *sock) {
|
||||
World::playerRemoved(sock);
|
||||
}
|
28
src/lua/LuaManager.hpp
Normal file
28
src/lua/LuaManager.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/CNProtocol.hpp"
|
||||
|
||||
#include <string>
|
||||
#ifdef _MSC_VER
|
||||
#include <luajit/lua.hpp>
|
||||
#else
|
||||
#include <lua.hpp>
|
||||
#endif
|
||||
|
||||
typedef int lRegistry;
|
||||
|
||||
namespace LuaManager {
|
||||
extern lua_State *global;
|
||||
void init();
|
||||
void printError(std::string err);
|
||||
|
||||
// runs the script in the passed file
|
||||
void runScript(std::string filename);
|
||||
void stopScripts();
|
||||
void loadScripts();
|
||||
|
||||
// unregisters the events tied to this state with all wrappers
|
||||
void clearState(lua_State *state);
|
||||
void playerAdded(CNSocket *sock);
|
||||
void playerRemoved(CNSocket *sock);
|
||||
}
|
223
src/lua/LuaWrapper.hpp
Normal file
223
src/lua/LuaWrapper.hpp
Normal file
@@ -0,0 +1,223 @@
|
||||
#pragma once
|
||||
/*
|
||||
This is a very simple header that adds a small "event" layer, where scripts can register callbacks & "waits" on events. The actual
|
||||
Event metatables & library is in EventWrapper.[hc]pp
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <cassert>
|
||||
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/PlayerWrapper.hpp"
|
||||
|
||||
#define yieldCall(state, nargs) \
|
||||
int _retCode = lua_resume(state, nargs); \
|
||||
if (_retCode != 0 && _retCode != LUA_YIELD) \
|
||||
LuaManager::printError(lua_tostring(state, -1)); \
|
||||
|
||||
inline static int lua_autoPush(lua_State* state, int nargs) {
|
||||
// return the number of pushed arguments :)
|
||||
return nargs;
|
||||
}
|
||||
|
||||
/*
|
||||
This function will automatically push all of the passed arguments onto the stack and returns the # of pushed values
|
||||
|
||||
Supported datatypes are:
|
||||
double or int : LUA_TNUMBER
|
||||
char* or const char* : LUA_TSTRING
|
||||
bool : LUA_TBOOLEAN
|
||||
lRegistry : grabs the object from the lua registry and pushes it onto the stack
|
||||
CNSocket* : Pushes the Player Entity
|
||||
*/
|
||||
template<typename T, class... Rest>
|
||||
inline static int lua_autoPush(lua_State* state, int nargs, T arg, Rest... rest) {
|
||||
// pick which branch to compile based on the type of arg
|
||||
if constexpr(std::is_same<T, int>::value || std::is_same<T, double>::value) {
|
||||
lua_pushnumber(state, (lua_Number)arg);
|
||||
} else if constexpr(std::is_same<T, char*>::value || std::is_same<T, const char*>::value) {
|
||||
lua_pushstring(state, (const char*)arg);
|
||||
} else if constexpr(std::is_same<T, lRegistry>::value) {
|
||||
// grab the value from the registry
|
||||
lua_rawgeti(state, LUA_REGISTRYINDEX, (int)arg);
|
||||
} else if constexpr(std::is_same<T, CNSocket*>::value) { // pushes a Player Entity
|
||||
LuaManager::Player::push(state, arg);
|
||||
} else if constexpr(std::is_same<T, bool>::value) {
|
||||
lua_pushboolean(state, arg);
|
||||
}
|
||||
|
||||
// recursively call, expanding rest and pushing the left-most rvalue into arg
|
||||
return lua_autoPush(state, ++nargs, rest...);
|
||||
}
|
||||
|
||||
enum eventType {
|
||||
EVENT_CALLBACK, // standard callback
|
||||
EVENT_WAIT // state needs to be resumed with the arguments
|
||||
};
|
||||
|
||||
class lEvent;
|
||||
|
||||
extern std::unordered_set<lEvent*> activeEvents;
|
||||
|
||||
class lEvent {
|
||||
private:
|
||||
struct rawEvent {
|
||||
lua_State *state;
|
||||
eventType type;
|
||||
lRegistry ref;
|
||||
bool disabled;
|
||||
};
|
||||
|
||||
uint32_t nextId; // next ID
|
||||
std::map<uint32_t, rawEvent> events;
|
||||
|
||||
uint32_t registerEvent(lua_State *state, lRegistry ref, eventType type) {
|
||||
uint32_t id = nextId++;
|
||||
|
||||
assert(nextId < UINT32_MAX);
|
||||
events[id] = {state, type, ref, false};
|
||||
return id;
|
||||
}
|
||||
|
||||
void freeEvent(rawEvent *event) {
|
||||
// the registry of all states is the same!
|
||||
luaL_unref(event->state, LUA_REGISTRYINDEX, event->ref);
|
||||
}
|
||||
|
||||
public:
|
||||
lEvent() {
|
||||
events = std::map<uint32_t, rawEvent>();
|
||||
nextId = 0; // start at 0
|
||||
activeEvents.insert(this);
|
||||
}
|
||||
|
||||
~lEvent() {
|
||||
// remove from the active set and disable all existing callbacks
|
||||
activeEvents.erase(this);
|
||||
clear();
|
||||
}
|
||||
|
||||
uint32_t addCallback(lua_State *state, lRegistry ref) {
|
||||
return registerEvent(state, ref, EVENT_CALLBACK);
|
||||
}
|
||||
|
||||
// yields the thread until the event is called
|
||||
uint32_t addWait(lua_State *state) {
|
||||
// push the thread onto the stack and register it in the global registry
|
||||
lua_pushthread(state);
|
||||
lRegistry ref = luaL_ref(state, LUA_REGISTRYINDEX);
|
||||
return registerEvent(state, ref, EVENT_WAIT);
|
||||
}
|
||||
|
||||
// walks through the events and unregister them from the state
|
||||
void clear() {
|
||||
for (auto &pair : events) {
|
||||
freeEvent(&pair.second);
|
||||
}
|
||||
|
||||
events.clear();
|
||||
}
|
||||
|
||||
// free the event based on id
|
||||
void clear(uint32_t id) {
|
||||
auto iter = events.find(id);
|
||||
|
||||
// sanity check
|
||||
if (iter == events.end())
|
||||
return;
|
||||
|
||||
// free the event
|
||||
freeEvent(&(*iter).second);
|
||||
events.erase(iter);
|
||||
}
|
||||
|
||||
// frees all events to this state
|
||||
void clear(lua_State *state) {
|
||||
for (auto iter = events.begin(); iter != events.end();) {
|
||||
rawEvent *event = &(*iter).second;
|
||||
|
||||
// if this is our state, free this event and erase it from the map
|
||||
if (event->state == state) {
|
||||
freeEvent(event);
|
||||
events.erase(iter++);
|
||||
} else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
void disconnectEvent(uint32_t id) {
|
||||
auto iter = events.find(id);
|
||||
|
||||
// sanity check
|
||||
if (iter == events.end())
|
||||
return;
|
||||
|
||||
(*iter).second.disabled = true;
|
||||
}
|
||||
|
||||
void reconnectEvent(uint32_t id) {
|
||||
auto iter = events.find(id);
|
||||
|
||||
// sanity check
|
||||
if (iter == events.end())
|
||||
return;
|
||||
|
||||
(*iter).second.disabled = false;
|
||||
}
|
||||
|
||||
bool isDisabled(uint32_t id) {
|
||||
auto iter = events.find(id);
|
||||
|
||||
// sanity check
|
||||
if (iter == events.end())
|
||||
return true;
|
||||
|
||||
return (*iter).second.disabled;
|
||||
}
|
||||
|
||||
template<class... Args> inline void call(Args... args) {
|
||||
auto eventsClone = events; // so if a callback is added, we don't iterate into undefined behavior
|
||||
for (auto &pair : eventsClone) {
|
||||
rawEvent *event = &pair.second;
|
||||
|
||||
// if the event is disabled, skip it
|
||||
if (event->disabled)
|
||||
continue;
|
||||
|
||||
switch (event->type) {
|
||||
case EVENT_CALLBACK: {
|
||||
// make thread for this callback
|
||||
if (!lua_checkstack(event->state, 1)) {
|
||||
std::cout << "[FATAL] Failed to create new lua thread! out of memory!" << std::endl;
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
lua_State *nThread = lua_newthread(event->state);
|
||||
|
||||
// push the callable first, the push all the arguments
|
||||
lua_rawgeti(nThread, LUA_REGISTRYINDEX, (int)event->ref);
|
||||
int nargs = lua_autoPush(nThread, 0, args...);
|
||||
|
||||
// then call it :)
|
||||
yieldCall(nThread, nargs);
|
||||
break;
|
||||
}
|
||||
case EVENT_WAIT: {
|
||||
// erase this event
|
||||
freeEvent(&pair.second);
|
||||
events.erase(pair.first);
|
||||
|
||||
// the :wait() will return the passed arguments
|
||||
int nargs = lua_autoPush(event->state, 0, args...);
|
||||
yieldCall(event->state, nargs);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::cout << "[WARN] INVALID EVENT TYPE : " << event->type << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
266
src/lua/NPCWrapper.cpp
Normal file
266
src/lua/NPCWrapper.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/NPCWrapper.hpp"
|
||||
|
||||
#include "NPC.hpp"
|
||||
#include "NPCManager.hpp"
|
||||
#include "Transport.hpp"
|
||||
|
||||
#define LIBNAME "NPC"
|
||||
#define NPCGONESTR "NPC was destoryed and no longer exists!"
|
||||
#define GETTERTBL "__npcGETTERS"
|
||||
#define SETTERTBL "__npcSETTERS"
|
||||
#define METHODTBL "__npcMETHODS"
|
||||
|
||||
static EntityRef* grabEntityRef(lua_State *state, int indx) {
|
||||
// first, make sure its a userdata
|
||||
luaL_checktype(state, indx, LUA_TUSERDATA);
|
||||
|
||||
// now, check and make sure its our library's metatable attached to this userdata
|
||||
EntityRef *ref = (EntityRef*)luaL_checkudata(state, indx, LIBNAME);
|
||||
if (ref == NULL) {
|
||||
luaL_typerror(state, indx, LIBNAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check if the npc exists still & return NULL if it doesn't
|
||||
if (!ref->isValid()) {
|
||||
luaL_argerror(state, indx, NPCGONESTR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
static CombatNPC* grabNPC(lua_State *state, int indx) {
|
||||
EntityRef *ref = grabEntityRef(state, indx);
|
||||
|
||||
// if grabEntityRef failed, return error result
|
||||
return (ref == NULL) ? NULL : (CombatNPC*)ref->getEntity();
|
||||
}
|
||||
|
||||
static int32_t grabID(lua_State *state, int indx) {
|
||||
EntityRef *ref = grabEntityRef(state, indx);
|
||||
|
||||
// if grabEntityRef failed, return error result
|
||||
return (ref == NULL) ? -1 : ref->id;
|
||||
}
|
||||
|
||||
// =============================================== [[ GETTERS ]] ===============================================
|
||||
|
||||
static int npc_getMaxHealth(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (npc == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushinteger(state, npc->maxHealth);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int npc_getSpeed(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
|
||||
// sanity check
|
||||
if (npc == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushinteger(state, npc->speed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg npc_getters[] = {
|
||||
{"maxHealth", npc_getMaxHealth},
|
||||
{"speed", npc_getSpeed},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// =============================================== [[ SETTERS ]] ===============================================
|
||||
|
||||
static int npc_setMaxHealth(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
int newMH = luaL_checkint(state, 2);
|
||||
|
||||
// sanity check
|
||||
if (npc != NULL)
|
||||
npc->maxHealth = newMH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npc_setSpeed(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
int newSpeed = luaL_checkint(state, 2);
|
||||
|
||||
// sanity check
|
||||
if (npc != NULL)
|
||||
npc->speed = newSpeed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg npc_setters[] = {
|
||||
{"maxHealth", npc_setMaxHealth},
|
||||
{"speed", npc_setSpeed},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// =============================================== [[ METHODS ]] ===============================================
|
||||
|
||||
static int npc_moveto(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
int X = luaL_checkint(state, 2);
|
||||
int Y = luaL_checkint(state, 3);
|
||||
int Z = luaL_checkint(state, 4);
|
||||
|
||||
// sanity check
|
||||
if (npc == NULL)
|
||||
return 0;
|
||||
|
||||
std::queue<Vec3> queue;
|
||||
Vec3 from = { npc->x, npc->y, npc->z };
|
||||
Vec3 to = { X, Y, Z };
|
||||
|
||||
// add a route to the queue; to be processed in Transport::stepNPCPathing()
|
||||
Transport::lerp(&queue, from, to, npc->speed);
|
||||
Transport::NPCQueues[npc->appearanceData.iNPC_ID] = queue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npc_say(lua_State *state) {
|
||||
CombatNPC *npc = grabNPC(state, 1);
|
||||
std::string msg = std::string(luaL_checkstring(state, 2));
|
||||
|
||||
// sanity check
|
||||
if (npc != NULL) {
|
||||
INITSTRUCT(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, res);
|
||||
|
||||
U8toU16(msg, (char16_t*)&res.szFreeChat, sizeof(res.szFreeChat));
|
||||
res.iPC_ID = npc->appearanceData.iNPC_ID;
|
||||
res.iEmoteCode = 421;
|
||||
|
||||
// send the packet
|
||||
NPCManager::sendToViewable(npc, &res, P_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC, sizeof(sP_FE2CL_REP_SEND_MENUCHAT_MESSAGE_SUCC));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg npc_methods[] = {
|
||||
{"moveTo", npc_moveto},
|
||||
{"say", npc_say},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// in charge of calling the correct getter method
|
||||
static int plr_index(lua_State *state) {
|
||||
// grab the function from the getters lookup table
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// if it's not nil, call it and run the getter method
|
||||
if (!lua_isnil(state, -1)) {
|
||||
// push userdata & call the function
|
||||
lua_pushvalue(state, 1);
|
||||
lua_call(state, 1, 1);
|
||||
|
||||
// return # of results
|
||||
return 1;
|
||||
}
|
||||
|
||||
// grab the function from the methods lookup table
|
||||
lua_pop(state, 1);
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// return result
|
||||
return 1;
|
||||
}
|
||||
|
||||
// in charge of calling the correct setter method
|
||||
static int plr_newindex(lua_State *state) {
|
||||
// grab the function from the getters lookup table
|
||||
lua_pushstring(state, SETTERTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// if it's nil return
|
||||
if (lua_isnil(state, -1))
|
||||
return 0;
|
||||
|
||||
// push userdata & call the function
|
||||
lua_pushvalue(state, 1);
|
||||
lua_call(state, 1, 0);
|
||||
|
||||
// return # of results
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npc_new(lua_State *state) {
|
||||
int X = luaL_checkint(state, 1);
|
||||
int Y = luaL_checkint(state, 2);
|
||||
int Z = luaL_checkint(state, 3);
|
||||
int type = luaL_checkint(state, 4);
|
||||
int id = NPCManager::nextId++;
|
||||
|
||||
// create & initalize the NPC
|
||||
CombatNPC *NPC = new CombatNPC(X, Y, Z, 0, INSTANCE_OVERWORLD, type, id, 1000);
|
||||
NPCManager::NPCs[id] = NPC;
|
||||
NPCManager::updateNPCPosition(id, X, Y, Z, INSTANCE_OVERWORLD, 0);
|
||||
|
||||
// push it to the lua stack & return
|
||||
LuaManager::NPC::push(state, NPC);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void LuaManager::NPC::init(lua_State *state) {
|
||||
// register our library as a global (and leave it on the stack)
|
||||
luaL_register(state, LIBNAME, npc_methods);
|
||||
|
||||
// sets NPC.new
|
||||
lua_pushstring(state, "new");
|
||||
lua_pushcfunction(state, npc_new);
|
||||
lua_rawset(state, -3);
|
||||
|
||||
// create the meta table and populate it with our functions
|
||||
luaL_newmetatable(state, LIBNAME);
|
||||
lua_pushstring(state, "__index");
|
||||
lua_pushcfunction(state, plr_index);
|
||||
lua_rawset(state, -3); // sets meta.__index = plr_index
|
||||
lua_pushstring(state, "__newindex");
|
||||
lua_pushcfunction(state, plr_newindex);
|
||||
lua_rawset(state, -3); // sets meta.__newindex = plr_newindex
|
||||
lua_pop(state, 2); // pop meta & library table
|
||||
|
||||
// create the methods table
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_newtable(state);
|
||||
Entity::addMethods(state); // register the base Entity methods
|
||||
luaL_register(state, NULL, npc_methods);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
// create the getters table
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_newtable(state);
|
||||
Entity::addGetters(state); // register the base Entity getters
|
||||
luaL_register(state, NULL, npc_getters);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
// create the setters table
|
||||
lua_pushstring(state, SETTERTBL);
|
||||
lua_newtable(state);
|
||||
luaL_register(state, NULL, npc_setters);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
LuaManager::Entity::registerSuper(state, LIBNAME);
|
||||
}
|
||||
|
||||
void LuaManager::NPC::push(lua_State *state, CombatNPC *npc) {
|
||||
Entity::push(state, EntityRef(npc->appearanceData.iNPC_ID), LIBNAME);
|
||||
}
|
11
src/lua/NPCWrapper.hpp
Normal file
11
src/lua/NPCWrapper.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/EntityWrapper.hpp"
|
||||
|
||||
namespace LuaManager {
|
||||
namespace NPC {
|
||||
void init(lua_State *state);
|
||||
|
||||
void push(lua_State *state, CombatNPC *npc);
|
||||
}
|
||||
}
|
278
src/lua/PlayerWrapper.cpp
Normal file
278
src/lua/PlayerWrapper.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "lua/EntityWrapper.hpp"
|
||||
#include "lua/EventWrapper.hpp"
|
||||
#include "lua/PlayerWrapper.hpp"
|
||||
|
||||
#include "core/CNProtocol.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
#include "Chat.hpp"
|
||||
|
||||
#define LIBNAME "Player"
|
||||
#define PLRGONESTR "Player doesn't exist anymore, they left!"
|
||||
#define GETTERTBL "__plrGETTERS"
|
||||
#define SETTERTBL "__plrSETTERS"
|
||||
#define METHODTBL "__plrMETHODS"
|
||||
|
||||
static EntityRef* grabEntityRef(lua_State *state, int indx) {
|
||||
// first, make sure its a userdata
|
||||
luaL_checktype(state, indx, LUA_TUSERDATA);
|
||||
|
||||
// now, check and make sure its our library's metatable attached to this userdata
|
||||
EntityRef *ref = (EntityRef*)luaL_checkudata(state, indx, LIBNAME);
|
||||
if (ref == NULL) {
|
||||
luaL_typerror(state, indx, LIBNAME);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check if the player exists still & return NULL if it doesn't
|
||||
if (!ref->isValid()) {
|
||||
luaL_argerror(state, indx, PLRGONESTR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
static Player* grabPlayer(lua_State *state, int indx) {
|
||||
EntityRef *ref = grabEntityRef(state, indx);
|
||||
|
||||
if (ref == NULL)
|
||||
return NULL;
|
||||
|
||||
return (Player*)ref->getEntity();
|
||||
}
|
||||
|
||||
static CNSocket* grabSock(lua_State *state, int indx) {
|
||||
EntityRef *ref = grabEntityRef(state, indx);
|
||||
|
||||
if (ref == NULL)
|
||||
return NULL;
|
||||
|
||||
return ref->sock;
|
||||
}
|
||||
|
||||
// =============================================== [[ GETTERS ]] ===============================================
|
||||
|
||||
static int plr_getName(lua_State *state) {
|
||||
Player *plr = grabPlayer(state, 1);
|
||||
|
||||
if (plr == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushstring(state, PlayerManager::getPlayerName(plr).c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int plr_getInstance(lua_State *state) {
|
||||
Player *plr = grabPlayer(state, 1);
|
||||
|
||||
if (plr == NULL)
|
||||
return 0;
|
||||
|
||||
lua_pushnumber(state, plr->instanceID);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int plr_getChatted(lua_State *state) {
|
||||
Player *plr = grabPlayer(state, 1);
|
||||
|
||||
if (plr == NULL)
|
||||
return 0;
|
||||
|
||||
// the Player* entity doesn't actually have an lEvent setup until a lua script asks for it, so
|
||||
// if Player->onChat is nullptr, create the lEvent and then push it :D
|
||||
if (plr->onChat == nullptr)
|
||||
plr->onChat = new lEvent();
|
||||
|
||||
LuaManager::Event::push(state, plr->onChat);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg plr_getters[] = {
|
||||
{"name", plr_getName},
|
||||
{"instance", plr_getInstance},
|
||||
{"onChat", plr_getChatted},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// =============================================== [[ SETTERS ]] ===============================================
|
||||
|
||||
static int plr_setInstance(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
int newInst = luaL_checkint(state, 2);
|
||||
Player *plr;
|
||||
CNSocket *sock;
|
||||
|
||||
// sanity check
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
plr = (Player*)ref->getEntity();
|
||||
sock = ref->sock;
|
||||
|
||||
PlayerManager::sendPlayerTo(sock, plr->x, plr->y, plr->z, newInst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg plr_setters[] = {
|
||||
{"instance", plr_setInstance},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// =============================================== [[ METHODS ]] ===============================================
|
||||
|
||||
static int plr_kick(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
Player *plr;
|
||||
CNSocket *sock;
|
||||
|
||||
// sanity check
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
plr = (Player*)ref->getEntity();
|
||||
sock = ref->sock;
|
||||
|
||||
// construct packet
|
||||
INITSTRUCT(sP_FE2CL_REP_PC_EXIT_SUCC, response);
|
||||
|
||||
response.iID = plr->iID;
|
||||
response.iExitCode = 3; // "a GM has terminated your connection"
|
||||
|
||||
// send to target player
|
||||
sock->sendPacket(response, P_FE2CL_REP_PC_EXIT_SUCC);
|
||||
|
||||
// ensure that the connection has terminated
|
||||
sock->kill();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plr_teleport(lua_State *state) {
|
||||
EntityRef *ref = grabEntityRef(state, 1);
|
||||
Player *plr;
|
||||
CNSocket *sock;
|
||||
|
||||
// sanity check
|
||||
if (ref == NULL)
|
||||
return 0;
|
||||
|
||||
plr = (Player*)ref->getEntity();
|
||||
sock = ref->sock;
|
||||
|
||||
int X = luaL_checkint(state, 2);
|
||||
int Y = luaL_checkint(state, 3);
|
||||
int Z = luaL_checkint(state, 4);
|
||||
int inst = luaL_optint(state, 5, plr->instanceID);
|
||||
|
||||
PlayerManager::sendPlayerTo(sock, X, Y, Z, inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int plr_msg(lua_State *state) {
|
||||
CNSocket *sock = grabSock(state, 1);
|
||||
const char *msg = luaL_checkstring(state, 2);
|
||||
|
||||
// sanity check
|
||||
if (sock != NULL)
|
||||
Chat::sendServerMessage(sock, std::string(msg));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const luaL_Reg plr_methods[] = {
|
||||
{"kick", plr_kick},
|
||||
{"teleport", plr_teleport},
|
||||
{"message", plr_msg},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// in charge of calling the correct getter method
|
||||
static int plr_index(lua_State *state) {
|
||||
// grab the function from the getters lookup table
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// if it's not nil, call it and run the getter method
|
||||
if (!lua_isnil(state, -1)) {
|
||||
// push userdata & call the function
|
||||
lua_pushvalue(state, 1);
|
||||
lua_call(state, 1, 1);
|
||||
|
||||
// return # of results
|
||||
return 1;
|
||||
}
|
||||
|
||||
// grab the function from the methods lookup table
|
||||
lua_pop(state, 1);
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// return result
|
||||
return 1;
|
||||
}
|
||||
|
||||
// in charge of calling the correct setter method
|
||||
static int plr_newindex(lua_State *state) {
|
||||
// grab the function from the getters lookup table
|
||||
lua_pushstring(state, SETTERTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// if it's nil return
|
||||
if (lua_isnil(state, -1))
|
||||
return 0;
|
||||
|
||||
// push userdata & call the function
|
||||
lua_pushvalue(state, 1);
|
||||
lua_call(state, 1, 0);
|
||||
|
||||
// return # of results
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LuaManager::Player::init(lua_State *state) {
|
||||
// register our library as a global (and leave it on the stack)
|
||||
luaL_register(state, LIBNAME, plr_methods);
|
||||
|
||||
// create the meta table and populate it with our functions
|
||||
luaL_newmetatable(state, LIBNAME);
|
||||
lua_pushstring(state, "__index");
|
||||
lua_pushcfunction(state, plr_index);
|
||||
lua_rawset(state, -3); // sets meta.__index = plr_index
|
||||
lua_pushstring(state, "__newindex");
|
||||
lua_pushcfunction(state, plr_newindex);
|
||||
lua_rawset(state, -3); // sets meta.__newindex = plr_newindex
|
||||
lua_pop(state, 2); // pop meta & library table
|
||||
|
||||
// create the methods table
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_newtable(state);
|
||||
Entity::addMethods(state); // register the base Entity methods
|
||||
luaL_register(state, NULL, plr_methods);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
// create the getters table
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_newtable(state);
|
||||
Entity::addGetters(state); // register the base Entity getters
|
||||
luaL_register(state, NULL, plr_getters);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
// create the setters table
|
||||
lua_pushstring(state, SETTERTBL);
|
||||
lua_newtable(state);
|
||||
luaL_register(state, NULL, plr_setters);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
LuaManager::Entity::registerSuper(state, LIBNAME);
|
||||
}
|
||||
|
||||
void LuaManager::Player::push(lua_State *state, CNSocket *sock) {
|
||||
Entity::push(state, EntityRef(sock), LIBNAME);
|
||||
}
|
11
src/lua/PlayerWrapper.hpp
Normal file
11
src/lua/PlayerWrapper.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/EntityWrapper.hpp"
|
||||
|
||||
namespace LuaManager {
|
||||
namespace Player {
|
||||
void init(lua_State *state);
|
||||
|
||||
void push(lua_State *state, CNSocket *sock);
|
||||
}
|
||||
}
|
130
src/lua/WorldWrapper.cpp
Normal file
130
src/lua/WorldWrapper.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "lua/LuaWrapper.hpp"
|
||||
#include "lua/EventWrapper.hpp"
|
||||
#include "lua/WorldWrapper.hpp"
|
||||
#include "core/CNStructs.hpp"
|
||||
|
||||
#include "PlayerManager.hpp"
|
||||
|
||||
static lEvent *addedEvent;
|
||||
static lEvent *removedEvent;
|
||||
|
||||
#define LIBNAME "World"
|
||||
#define GETTERTBL "__wrldGETTERS"
|
||||
#define METHODTBL "__wrldMETHODS"
|
||||
|
||||
// =============================================== [[ GETTERS ]] ===============================================
|
||||
|
||||
int wrld_getPlrAdded(lua_State *state) {
|
||||
LuaManager::Event::push(state, addedEvent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wrld_getPlrRemoved(lua_State *state) {
|
||||
LuaManager::Event::push(state, removedEvent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wrld_getVersion(lua_State *state) {
|
||||
lua_pushnumber(state, PROTOCOL_VERSION);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int wrld_getPlayers(lua_State *state) {
|
||||
// create a new lua table and push it onto the stack
|
||||
int entries = 0;
|
||||
lua_newtable(state);
|
||||
|
||||
// walk through the current list of players and add them to the table
|
||||
for (auto pair : PlayerManager::players) {
|
||||
lua_pushinteger(state, ++entries);
|
||||
LuaManager::Player::push(state, pair.first);
|
||||
lua_rawset(state, -3);
|
||||
}
|
||||
|
||||
// returns the player table :)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// =============================================== [[ METHODS ]] ===============================================
|
||||
|
||||
int wrld_index(lua_State *state) {
|
||||
// grab the function from the getters lookup table
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// if it's not nil, call it and run the getter method
|
||||
if (!lua_isnil(state, -1)) {
|
||||
// push userdata & call the function
|
||||
lua_pushvalue(state, 1);
|
||||
lua_call(state, 1, 1);
|
||||
|
||||
// return # of results
|
||||
return 1;
|
||||
}
|
||||
|
||||
// grab the function from the methods lookup table
|
||||
lua_pop(state, 1);
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_rawget(state, LUA_REGISTRYINDEX);
|
||||
lua_pushvalue(state, 2);
|
||||
lua_rawget(state, -2);
|
||||
|
||||
// return result
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg getters[] {
|
||||
{"onPlayerAdded", wrld_getPlrAdded},
|
||||
{"onPlayerRemoved", wrld_getPlrRemoved},
|
||||
{"players", wrld_getPlayers},
|
||||
{"version", wrld_getVersion},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
// TODO
|
||||
static const luaL_Reg methods[] = {
|
||||
//{"getNearbyPlayers", wrld_getNPlrs},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
|
||||
void LuaManager::World::init(lua_State *state) {
|
||||
lua_newtable(state);
|
||||
luaL_newmetatable(state, LIBNAME);
|
||||
lua_pushstring(state, "__index");
|
||||
lua_pushcfunction(state, wrld_index);
|
||||
lua_rawset(state, -3); // sets meta.__index = wrld_index
|
||||
lua_setmetatable(state, -2); // sets world.__metatable = meta
|
||||
lua_setglobal(state, LIBNAME);
|
||||
|
||||
// setup the __wrldGETTERS table in the registry
|
||||
lua_pushstring(state, GETTERTBL);
|
||||
lua_newtable(state);
|
||||
luaL_register(state, NULL, getters);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
// setup the __wrldMETHODS table in the registry
|
||||
lua_pushstring(state, METHODTBL);
|
||||
lua_newtable(state);
|
||||
luaL_register(state, NULL, methods);
|
||||
lua_rawset(state, LUA_REGISTRYINDEX);
|
||||
|
||||
addedEvent = new lEvent();
|
||||
removedEvent = new lEvent();
|
||||
}
|
||||
|
||||
void LuaManager::World::clearState(lua_State *state) {
|
||||
addedEvent->clear(state);
|
||||
removedEvent->clear(state);
|
||||
}
|
||||
|
||||
void LuaManager::World::playerAdded(CNSocket *sock) {
|
||||
addedEvent->call(sock);
|
||||
}
|
||||
|
||||
void LuaManager::World::playerRemoved(CNSocket *sock) {
|
||||
removedEvent->call(sock);
|
||||
}
|
13
src/lua/WorldWrapper.hpp
Normal file
13
src/lua/WorldWrapper.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "lua/LuaManager.hpp"
|
||||
|
||||
namespace LuaManager {
|
||||
namespace World {
|
||||
void init(lua_State *state);
|
||||
|
||||
void clearState(lua_State *state);
|
||||
void playerAdded(CNSocket *sock);
|
||||
void playerRemoved(CNSocket *sock);
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#include "servers/CNLoginServer.hpp"
|
||||
#include "servers/CNShardServer.hpp"
|
||||
#include "lua/LuaManager.hpp"
|
||||
#include "PlayerManager.hpp"
|
||||
#include "PlayerMovement.hpp"
|
||||
#include "BuiltinCommands.hpp"
|
||||
@@ -120,6 +121,7 @@ int main() {
|
||||
Racing::init();
|
||||
Database::open();
|
||||
Trading::init();
|
||||
LuaManager::init();
|
||||
|
||||
switch (settings::EVENTMODE) {
|
||||
case 0: break; // no event
|
||||
|
@@ -35,6 +35,7 @@ int settings::SPAWN_ANGLE = 130;
|
||||
std::string settings::DBPATH = "database.db";
|
||||
std::string settings::TDATADIR = "tdata/";
|
||||
std::string settings::PATCHDIR = "tdata/patch/";
|
||||
std::string settings::SCRIPTSDIR = "scripts";
|
||||
|
||||
std::string settings::NPCJSON = "NPCs.json";
|
||||
std::string settings::MOBJSON = "mobs.json";
|
||||
|
@@ -27,6 +27,7 @@ namespace settings {
|
||||
extern std::string PATCHDIR;
|
||||
extern std::string ENABLEDPATCHES;
|
||||
extern std::string TDATADIR;
|
||||
extern std::string SCRIPTSDIR;
|
||||
extern int EVENTMODE;
|
||||
extern bool MONITORENABLED;
|
||||
extern int MONITORPORT;
|
||||
|
Reference in New Issue
Block a user