testmodules/test1: Shows a 3D scene controllable by user input; add and fix other things to support it
This commit is contained in:
parent
76778cd2e7
commit
d934d02f6e
@ -67,6 +67,7 @@ dofile(__buildat_get_path("share").."/client/test.lua")
|
||||
dofile(__buildat_get_path("share").."/client/packet.lua")
|
||||
dofile(__buildat_get_path("share").."/client/extensions.lua")
|
||||
dofile(__buildat_get_path("share").."/client/sandbox.lua")
|
||||
dofile(__buildat_get_path("share").."/client/urho3d.lua")
|
||||
|
||||
local test = require("buildat/extension/test")
|
||||
test.f()
|
||||
|
@ -78,16 +78,29 @@ __buildat_sandbox_environment.require = function(name)
|
||||
error("require: \""..name.."\" not found in sandbox")
|
||||
end
|
||||
|
||||
local function run_in_sandbox(untrusted_code, sandbox)
|
||||
if untrusted_code:byte(1) == 27 then return false, "binary bytecode prohibited" end
|
||||
local untrusted_function, message = loadstring(untrusted_code)
|
||||
if not untrusted_function then return false, message end
|
||||
local function run_function_in_sandbox(untrusted_function, sandbox)
|
||||
setfenv(untrusted_function, sandbox)
|
||||
return __buildat_pcall(untrusted_function)
|
||||
end
|
||||
|
||||
function __buildat_run_in_sandbox(untrusted_code)
|
||||
local status, err = run_in_sandbox(untrusted_code, __buildat_sandbox_environment)
|
||||
function __buildat_run_function_in_sandbox(untrusted_function)
|
||||
local status, err = run_function_in_sandbox(untrusted_function, __buildat_sandbox_environment)
|
||||
if status == false then
|
||||
log:error("Failed to run function:\n"..err)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function run_code_in_sandbox(untrusted_code, sandbox)
|
||||
if untrusted_code:byte(1) == 27 then return false, "binary bytecode prohibited" end
|
||||
local untrusted_function, message = loadstring(untrusted_code)
|
||||
if not untrusted_function then return false, message end
|
||||
return run_function_in_sandbox(untrusted_function, sandbox)
|
||||
end
|
||||
|
||||
function __buildat_run_code_in_sandbox(untrusted_code)
|
||||
local status, err = run_code_in_sandbox(untrusted_code, __buildat_sandbox_environment)
|
||||
if status == false then
|
||||
log:error("Failed to run script:\n"..err)
|
||||
return false
|
||||
@ -102,5 +115,7 @@ function buildat:run_script_file(name)
|
||||
return false
|
||||
end
|
||||
log:info("buildat:run_script_file("..name.."): code length: "..#code)
|
||||
return __buildat_run_in_sandbox(code)
|
||||
return __buildat_run_code_in_sandbox(code)
|
||||
end
|
||||
|
||||
log:info("sandbox.lua loaded")
|
||||
|
69
client/urho3d.lua
Normal file
69
client/urho3d.lua
Normal file
@ -0,0 +1,69 @@
|
||||
-- Buildat: client/urho3d.lua
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
-- Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
local log = buildat.Logger("client/urho3d")
|
||||
|
||||
local sandbox = __buildat_sandbox_environment
|
||||
|
||||
-- Set every plain value in global environment to the sandbox
|
||||
-- ...it's maybe safe enough...
|
||||
for k, v in pairs(_G) do
|
||||
if type(v) == 'number' or type(v) == 'string' then
|
||||
--log:info("Setting sandbox["..k.."] = "..buildat.dump(v))
|
||||
sandbox[k] = _G[k]
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Require explicit whitelisting of classes, method/function argument and
|
||||
-- property types
|
||||
|
||||
local safe_globals = {
|
||||
-- Instances
|
||||
"cache",
|
||||
"ui",
|
||||
"renderer",
|
||||
"input",
|
||||
-- Types
|
||||
"Scene",
|
||||
"Text",
|
||||
"Color",
|
||||
"Vector3",
|
||||
"Quaternion",
|
||||
"Viewport",
|
||||
"CustomGeometry",
|
||||
"Texture",
|
||||
"Material",
|
||||
-- Functions
|
||||
"Random",
|
||||
"Clamp",
|
||||
-- WTF properties
|
||||
"KEY_W",
|
||||
"KEY_S",
|
||||
"KEY_A",
|
||||
"KEY_D",
|
||||
}
|
||||
|
||||
for _, v in ipairs(safe_globals) do
|
||||
sandbox[v] = _G[v]
|
||||
end
|
||||
|
||||
local sandbox_function_name_to_global_function_name = {}
|
||||
local next_global_function_i = 1
|
||||
|
||||
function sandbox.SubscribeToEvent(event_name, function_name)
|
||||
if type(sandbox[function_name]) ~= 'function' then
|
||||
error("sandbox.SubscribeToEvent(): '"..function_name..
|
||||
"' is not a global function in sandbox environment")
|
||||
end
|
||||
local global_function_i = next_global_function_i
|
||||
next_global_function_i = next_global_function_i + 1
|
||||
local global_function_name = "__buildat_sandbox_callback_"..global_function_i
|
||||
sandbox_function_name_to_global_function_name[function_name] = global_function_name
|
||||
_G[global_function_name] = function(eventType, eventData)
|
||||
local f = function()
|
||||
sandbox[function_name](eventType, eventData)
|
||||
end
|
||||
__buildat_run_function_in_sandbox(f)
|
||||
end
|
||||
SubscribeToEvent(event_name, global_function_name)
|
||||
end
|
@ -31,7 +31,7 @@ namespace app {
|
||||
struct CApp: public App, public u3d::Application
|
||||
{
|
||||
sp_<client::State> m_state;
|
||||
up_<u3d::LuaScript> m_script;
|
||||
u3d::LuaScript *m_script;
|
||||
lua_State *L;
|
||||
int64_t m_last_script_tick_us;
|
||||
|
||||
@ -40,11 +40,79 @@ struct CApp: public App, public u3d::Application
|
||||
m_script(nullptr),
|
||||
L(nullptr),
|
||||
m_last_script_tick_us(get_timeofday_us())
|
||||
{
|
||||
engineParameters_["WindowTitle"] = "Buildat Client";
|
||||
engineParameters_["LogName"] = "client_Urho3D.log";
|
||||
engineParameters_["FullScreen"] = false;
|
||||
engineParameters_["Headless"] = false;
|
||||
// TODO: Proper paths
|
||||
engineParameters_["ResourcePaths"] =
|
||||
"/home/celeron55/softat/Urho3D/Bin/CoreData;"
|
||||
"/home/celeron55/softat/Urho3D/Bin/Data";
|
||||
engineParameters_["AutoloadPaths"] = "";
|
||||
|
||||
// Set up on_update event (this runs every frame)
|
||||
SubscribeToEvent(u3d::E_UPDATE, HANDLER(CApp, on_update));
|
||||
|
||||
// TODO: Set up input events (Call stuff like
|
||||
// call_global_if_exists(L, "__buildat_key_down", 1, 0);)
|
||||
// ...or don't? They should be available in Urho3D API.
|
||||
}
|
||||
|
||||
~CApp()
|
||||
{
|
||||
}
|
||||
|
||||
void set_state(sp_<client::State> state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
int run()
|
||||
{
|
||||
return u3d::Application::Run();
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
u3d::Engine *engine = GetSubsystem<u3d::Engine>();
|
||||
engine->Exit();
|
||||
}
|
||||
|
||||
void run_script(const ss_ &script)
|
||||
{
|
||||
log_v(MODULE, "run_script(): %s", cs(script));
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_run_code_in_sandbox");
|
||||
lua_pushlstring(L, script.c_str(), script.size());
|
||||
error_logging_pcall(L, 1, 1);
|
||||
bool status = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if(status == false){
|
||||
log_w(MODULE, "run_script(): failed");
|
||||
} else {
|
||||
log_v(MODULE, "run_script(): succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_packet(const ss_ &name, const ss_ &data)
|
||||
{
|
||||
log_v(MODULE, "handle_packet(): %s", cs(name));
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_handle_packet");
|
||||
lua_pushlstring(L, name.c_str(), name.size());
|
||||
lua_pushlstring(L, data.c_str(), data.size());
|
||||
error_logging_pcall(L, 2, 0);
|
||||
}
|
||||
|
||||
// Non-public methods
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Instantiate and register the Lua script subsystem so that we can use the LuaScriptInstance component
|
||||
context_->RegisterSubsystem(new u3d::LuaScript(context_));
|
||||
|
||||
m_script.reset(new u3d::LuaScript(context_));
|
||||
m_script = context_->GetSubsystem<u3d::LuaScript>();
|
||||
L = m_script->GetState();
|
||||
if(L == nullptr)
|
||||
throw Exception("m_script.GetState() returned null");
|
||||
@ -72,72 +140,9 @@ struct CApp: public App, public u3d::Application
|
||||
lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
engineParameters_["WindowTitle"] = "Buildat Client";
|
||||
engineParameters_["LogName"] = "client_Urho3D.log";
|
||||
engineParameters_["FullScreen"] = false;
|
||||
engineParameters_["Headless"] = false;
|
||||
// TODO
|
||||
engineParameters_["ResourcePaths"] =
|
||||
"/home/celeron55/softat/Urho3D/Bin/CoreData";
|
||||
engineParameters_["ResourcePackages"] = "";
|
||||
engineParameters_["AutoloadPaths"] = "../../../Urho3D/Bin/Data";
|
||||
|
||||
// TODO: Set up update() event
|
||||
SubscribeToEvent(u3d::E_UPDATE, HANDLER(CApp, on_update));
|
||||
|
||||
// TODO: Set up input events (Call stuff like
|
||||
// call_global_if_exists(L, "__buildat_key_down", 1, 0);)
|
||||
//m_script->ExecuteFile(u3d::String(init_lua_path.c_str()));
|
||||
}
|
||||
|
||||
~CApp()
|
||||
{
|
||||
}
|
||||
|
||||
void set_state(sp_<client::State> state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
int run()
|
||||
{
|
||||
return u3d::Application::Run();
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
u3d::Engine *engine = GetSubsystem<u3d::Engine>();
|
||||
engine->Exit();
|
||||
}
|
||||
|
||||
void run_script(const ss_ &script)
|
||||
{
|
||||
log_v(MODULE, "run_script(): %s", cs(script));
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_run_in_sandbox");
|
||||
lua_pushlstring(L, script.c_str(), script.size());
|
||||
error_logging_pcall(L, 1, 1);
|
||||
bool status = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if(status == false){
|
||||
log_w(MODULE, "run_script(): failed");
|
||||
} else {
|
||||
log_v(MODULE, "run_script(): succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_packet(const ss_ &name, const ss_ &data)
|
||||
{
|
||||
log_v(MODULE, "handle_packet(): %s", cs(name));
|
||||
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "__buildat_handle_packet");
|
||||
lua_pushlstring(L, name.c_str(), name.size());
|
||||
lua_pushlstring(L, data.c_str(), data.size());
|
||||
error_logging_pcall(L, 2, 0);
|
||||
}
|
||||
|
||||
// Non-public methods
|
||||
|
||||
void on_update(u3d::StringHash eventType, u3d::VariantMap &eventData)
|
||||
{
|
||||
if(g_sigint_received)
|
||||
@ -338,7 +343,7 @@ struct CApp: public App, public u3d::Application
|
||||
// pcall(untrusted_function) -> status, error
|
||||
static int l_pcall(lua_State *L)
|
||||
{
|
||||
log_v(MODULE, "l_pcall()");
|
||||
log_d(MODULE, "l_pcall()");
|
||||
lua_pushcfunction(L, handle_error);
|
||||
int handle_error_stack_i = lua_gettop(L);
|
||||
|
||||
@ -346,7 +351,7 @@ struct CApp: public App, public u3d::Application
|
||||
int r = lua_pcall(L, 0, 0, handle_error_stack_i);
|
||||
int error_stack_i = lua_gettop(L);
|
||||
if(r == 0){
|
||||
log_v(MODULE, "l_pcall() returned 0 (no error)");
|
||||
log_d(MODULE, "l_pcall() returned 0 (no error)");
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
@ -47,9 +47,9 @@ struct Module: public interface::Module
|
||||
m_server->load_module("client_data", builtin+"/client_data");
|
||||
|
||||
sv_<ss_> load_list = {
|
||||
//"test1",
|
||||
"test1",
|
||||
//"test2",
|
||||
"minigame",
|
||||
//"minigame",
|
||||
};
|
||||
for(const ss_ &name : load_list){
|
||||
m_server->load_module(name, m_server->get_modules_path()+"/"+name);
|
||||
|
@ -5,21 +5,41 @@ local log = buildat.Logger("test1")
|
||||
local dump = buildat.dump
|
||||
log:info("test1/init.lua loaded")
|
||||
|
||||
-- Test extension interface safety
|
||||
local test = require("buildat/extension/test")
|
||||
-- 3D things
|
||||
|
||||
test.f()
|
||||
-- NOTE: Create global variable so that it doesn't get automatically deleted
|
||||
scene_ = Scene()
|
||||
scene_:CreateComponent("Octree")
|
||||
|
||||
-- Test some 3D things
|
||||
local graphics = require("buildat/extension/graphics")
|
||||
-- Note that naming the scene nodes is optional
|
||||
local plane_node = scene_:CreateChild("Plane")
|
||||
plane_node.scale = Vector3(10.0, 1.0, 10.0)
|
||||
local plane_object = plane_node:CreateComponent("StaticModel")
|
||||
plane_object.model = cache:GetResource("Model", "Models/Plane.mdl")
|
||||
plane_object.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
|
||||
|
||||
scene = graphics.Scene(graphics.Scene.SCENE_3D)
|
||||
ground = graphics.ScenePrimitive(graphics.ScenePrimitive.TYPE_PLANE, 5,5)
|
||||
ground:loadTexture("test1/green_texture.png")
|
||||
scene:addEntity(ground)
|
||||
local light_node = scene_:CreateChild("DirectionalLight")
|
||||
light_node.direction = Vector3(0.6, -1.0, 0.8) -- The direction vector does not need to be normalized
|
||||
local light = light_node:CreateComponent("Light")
|
||||
light.lightType = LIGHT_DIRECTIONAL
|
||||
|
||||
scene:getDefaultCamera():setPosition(7,7,7)
|
||||
scene:getDefaultCamera():lookAt(graphics.Vector3(0,0,0), graphics.Vector3(0,1,0))
|
||||
-- Add a camera so we can look at the scene
|
||||
camera_node = scene_:CreateChild("Camera")
|
||||
camera_node:CreateComponent("Camera")
|
||||
camera_node.position = Vector3(7.0, 7.0, 7.0)
|
||||
--camera_node.rotation = Quaternion(0, 0, 0.0)
|
||||
camera_node:LookAt(Vector3(0, 1, 0))
|
||||
-- And this thing so the camera is shown on the screen
|
||||
local viewport = Viewport:new(scene_, camera_node:GetComponent("Camera"))
|
||||
renderer:SetViewport(0, viewport)
|
||||
|
||||
-- Add some text
|
||||
local title_text = ui.root:CreateChild("Text")
|
||||
title_text:SetText("test1/init.lua")
|
||||
title_text:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15)
|
||||
title_text.horizontalAlignment = HA_CENTER
|
||||
title_text.verticalAlignment = VA_CENTER
|
||||
title_text:SetPosition(0, ui.root.height*(-0.33))
|
||||
|
||||
local cereal = require("buildat/extension/cereal")
|
||||
|
||||
@ -41,11 +61,43 @@ buildat.sub_packet("test1:add_box", function(data)
|
||||
local y = values.y
|
||||
local z = values.z
|
||||
log:info("values="..dump(values))
|
||||
box = graphics.ScenePrimitive(graphics.ScenePrimitive.TYPE_BOX, w,h,d)
|
||||
box:loadTexture("test1/pink_texture.png")
|
||||
box:setPosition(x, y, z)
|
||||
scene:addEntity(box)
|
||||
the_box = box
|
||||
|
||||
--
|
||||
-- Mission: Make a box
|
||||
--
|
||||
|
||||
-- 1) Make the entity in the scene
|
||||
local node = scene_:CreateChild("THE GLORIOUS BOX")
|
||||
the_box = node
|
||||
node.scale = Vector3(w, h, d)
|
||||
node.position = Vector3(x, y, z)
|
||||
|
||||
-- 2) Create a StaticModel which kind of is what we need
|
||||
local object = node:CreateComponent("StaticModel")
|
||||
|
||||
-- 3) First it needs a model. We could just load this binaray blob:
|
||||
object.model = cache:GetResource("Model", "Models/Box.mdl")
|
||||
-- TODO: let's not. Let's generate some geometry!
|
||||
--[[local cc = CustomGeometry()
|
||||
cc.SetNumGeometries(1)
|
||||
cc.BeginGeometry(0, TRIANGLE_STRIP)
|
||||
cc.DefineVertex(]]
|
||||
|
||||
-- 4) Create a material. Again we could just load it from a file:
|
||||
--object.material = cache:GetResource("Material", "Materials/Stone.xml"):Clone()
|
||||
-- ...but let's create a material ourselves:
|
||||
g_m = Material() -- It has to be global because deletion causes a crash
|
||||
object.material = g_m
|
||||
-- We use this Diff.xml file to define that we want diffuse rendering. It
|
||||
-- doesn't make much sense to define it ourselves as it consists of quite many
|
||||
-- parameters:
|
||||
object.material:SetTechnique(0, cache:GetResource("Technique", "Techniques/Diff.xml"))
|
||||
-- And load the texture from a file:
|
||||
object.material:SetTexture(TU_DIFFUSE, cache:GetResource("Texture2D", "Textures/LogoLarge.png"))
|
||||
|
||||
--
|
||||
-- Make a non-useful but nice reply packet and send it to the server
|
||||
--
|
||||
|
||||
values = {
|
||||
a = 128,
|
||||
@ -82,44 +134,40 @@ buildat.sub_packet("test1:add_box", function(data)
|
||||
assert(values.f.y == 2)
|
||||
end)
|
||||
|
||||
local keyinput = require("buildat/extension/keyinput")
|
||||
function move_box_by_user_input(dt)
|
||||
-- Do not move if the UI has a focused element (the console)
|
||||
if ui.focusElement ~= nil then
|
||||
return
|
||||
end
|
||||
|
||||
keyinput.sub(function(key, state)
|
||||
log:info("key: "..key.." "..state)
|
||||
if key == keyinput.KEY_SPACE then
|
||||
if state == "down" then
|
||||
the_box:setPosition(0.0, 1.0, 0.0)
|
||||
scene:addEntity(box)
|
||||
end
|
||||
local MOVE_SPEED = 6.0
|
||||
local MOUSE_SENSITIVITY = 0.01
|
||||
|
||||
if the_box then
|
||||
local p = the_box.position
|
||||
p.y = Clamp(p.y - input.mouseMove.y * MOUSE_SENSITIVITY, 0, 5)
|
||||
the_box.position = p -- Needed?
|
||||
end
|
||||
end)
|
||||
|
||||
local mouseinput = require("buildat/extension/mouseinput")
|
||||
if input:GetKeyDown(KEY_W) then
|
||||
the_box:Translate(Vector3(-1.0, 0.0, 0.0) * MOVE_SPEED * dt)
|
||||
end
|
||||
if input:GetKeyDown(KEY_S) then
|
||||
the_box:Translate(Vector3(1.0, 0.0, 0.0) * MOVE_SPEED * dt)
|
||||
end
|
||||
if input:GetKeyDown(KEY_A) then
|
||||
the_box:Translate(Vector3(0.0, 0.0, -1.0) * MOVE_SPEED * dt)
|
||||
end
|
||||
if input:GetKeyDown(KEY_D) then
|
||||
the_box:Translate(Vector3(0.0, 0.0, 1.0) * MOVE_SPEED * dt)
|
||||
end
|
||||
end
|
||||
|
||||
mouseinput.sub_move(function(x, y)
|
||||
--log:info("mouse move: "..x..", "..y..")")
|
||||
end)
|
||||
mouseinput.sub_down(function(button, x, y)
|
||||
--log:info("mouse down: "..button..", "..x..", "..y..")")
|
||||
end)
|
||||
mouseinput.sub_up(function(button, x, y)
|
||||
--log:info("mouse up: "..button..", "..x..", "..y..")")
|
||||
end)
|
||||
function handle_update(eventType, eventData)
|
||||
--log:info("handle_update() in test1/init.lua")
|
||||
local dt = eventData:GetFloat("TimeStep")
|
||||
--node:Rotate(Quaternion(50, 80*dt, 0, 0))
|
||||
move_box_by_user_input(dt)
|
||||
end
|
||||
SubscribeToEvent("Update", "handle_update")
|
||||
|
||||
buildat.sub_packet("test1:array", function(data)
|
||||
local array = cereal.binary_input(data, {"array", "int32_t"})
|
||||
log:info("test1:array: "..dump(array))
|
||||
|
||||
data = cereal.binary_output(array, {"array", "int32_t"})
|
||||
buildat.send_packet("test1:array_response", data)
|
||||
end)
|
||||
|
||||
--[[
|
||||
-- Temporary test
|
||||
require "Polycode/Core"
|
||||
scene = Scene(Scene.SCENE_2D)
|
||||
scene:getActiveCamera():setOrthoSize(640, 480)
|
||||
label = SceneLabel("Hello from remote module!", 32)
|
||||
label:setPosition(0, -100, 0)
|
||||
scene:addChild(label)
|
||||
--]]
|
||||
|
4
todo.txt
4
todo.txt
@ -17,3 +17,7 @@ Buildat TODO
|
||||
- Move self-contained library-like Lua functions from client/app.cpp to a
|
||||
reusable location so that they can be used from server-side Lua in the future
|
||||
- util/codestyle.sh: Add some CMakeLists.txt formatting (case, whitespace)
|
||||
- Make two extensions for urho3d, one that loads into a table, and one that
|
||||
uses the table one and puts it in the global namespace. Then add engine
|
||||
support for automatically clearing them from the glboal namespace - some kind
|
||||
of a global namespace wrapper table?
|
||||
|
Loading…
x
Reference in New Issue
Block a user