mainly work on object scripting api
This commit is contained in:
parent
eef7bc3570
commit
9778347c7f
|
@ -1,7 +1,7 @@
|
|||
-- Client-side code of the test lua object
|
||||
|
||||
--
|
||||
-- Some helper functions
|
||||
-- Some helper functions and classes
|
||||
--
|
||||
|
||||
function split(str, pat)
|
||||
|
@ -23,6 +23,7 @@ function split(str, pat)
|
|||
return t
|
||||
end
|
||||
|
||||
-- For debugging
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
|
@ -36,29 +37,90 @@ function dump(o)
|
|||
end
|
||||
end
|
||||
|
||||
function vector_subtract(a, b)
|
||||
return {X=a.X-b.X, Y=a.Y-b.Y, Z=a.Z-b.Z}
|
||||
end
|
||||
|
||||
function vector_add(a, b)
|
||||
return {X=a.X+b.X, Y=a.Y+b.Y, Z=a.Z+b.Z}
|
||||
end
|
||||
|
||||
function vector_multiply(a, d)
|
||||
return {X=a.X*d, Y=a.Y*d, Z=a.Z*d}
|
||||
end
|
||||
|
||||
SmoothTranslator = {}
|
||||
SmoothTranslator.__index = SmoothTranslator
|
||||
|
||||
function SmoothTranslator.create()
|
||||
local obj = {}
|
||||
setmetatable(obj, SmoothTranslator)
|
||||
obj.vect_old = {X=0, Y=0, Z=0}
|
||||
obj.anim_counter = 0
|
||||
obj.anim_time = 0
|
||||
obj.anim_time_counter = 0
|
||||
obj.vect_show = {X=0, Y=0, Z=0}
|
||||
obj.vect_aim = {X=0, Y=0, Z=0}
|
||||
return obj
|
||||
end
|
||||
|
||||
function SmoothTranslator:update(vect_new)
|
||||
self.vect_old = self.vect_show
|
||||
self.vect_aim = vect_new
|
||||
if self.anim_time < 0.001 or self.anim_time > 1.0 then
|
||||
self.anim_time = self.anim_time_counter
|
||||
else
|
||||
self.anim_time = self.anim_time * 0.9 + self.anim_time_counter * 0.1
|
||||
end
|
||||
self.anim_time_counter = 0
|
||||
self.anim_counter = 0
|
||||
end
|
||||
|
||||
function SmoothTranslator:translate(dtime)
|
||||
self.anim_time_counter = self.anim_time_counter + dtime
|
||||
self.anim_counter = self.anim_counter + dtime
|
||||
vect_move = vector_subtract(self.vect_aim, self.vect_old)
|
||||
moveratio = 1.0
|
||||
if self.anim_time > 0.001 then
|
||||
moveratio = self.anim_time_counter / self.anim_time
|
||||
end
|
||||
if moveratio > 1.5 then
|
||||
moveratio = 1.5
|
||||
end
|
||||
self.vect_show = vector_add(self.vect_old, vector_multiply(vect_move, moveratio))
|
||||
end
|
||||
|
||||
--
|
||||
-- Actual code
|
||||
--
|
||||
|
||||
function step(self, dtime)
|
||||
-- Some smoother animation could be done here
|
||||
pos_trans = SmoothTranslator.create()
|
||||
rot_trans = SmoothTranslator.create()
|
||||
|
||||
-- Callback functions
|
||||
|
||||
function on_step(self, dtime)
|
||||
pos_trans:translate(dtime)
|
||||
rot_trans:translate(dtime)
|
||||
object_set_position(self, pos_trans.vect_show)
|
||||
object_set_rotation(self, rot_trans.vect_show)
|
||||
end
|
||||
|
||||
function process_message(self, data)
|
||||
function on_process_message(self, data)
|
||||
--print("client got message: " .. data)
|
||||
|
||||
-- Receive our custom messages
|
||||
|
||||
sp = split(data, " ")
|
||||
if sp[1] == "pos" then
|
||||
object_set_position(self, sp[2], sp[3], sp[4])
|
||||
pos_trans:update({X=sp[2], Y=sp[3], Z=sp[4]})
|
||||
end
|
||||
if sp[1] == "rot" then
|
||||
object_set_rotation(self, sp[2], sp[3], sp[4])
|
||||
rot_trans:update({X=sp[2], Y=sp[3], Z=sp[4]})
|
||||
end
|
||||
end
|
||||
|
||||
function initialize(self, data)
|
||||
function on_initialize(self, data)
|
||||
print("client object got initialization: " .. data)
|
||||
|
||||
corners = {
|
||||
|
|
|
@ -1,24 +1,42 @@
|
|||
-- Server-side code of the test lua object
|
||||
|
||||
--
|
||||
-- Some helper functions and classes
|
||||
--
|
||||
|
||||
function vector_subtract(a, b)
|
||||
return {X=a.X-b.X, Y=a.Y-b.Y, Z=a.Z-b.Z}
|
||||
end
|
||||
|
||||
function vector_add(a, b)
|
||||
return {X=a.X+b.X, Y=a.Y+b.Y, Z=a.Z+b.Z}
|
||||
end
|
||||
|
||||
function vector_multiply(a, d)
|
||||
return {X=a.X*d, Y=a.Y*d, Z=a.Z*d}
|
||||
end
|
||||
|
||||
--
|
||||
-- Actual code
|
||||
--
|
||||
|
||||
counter = 0
|
||||
counter2 = 0
|
||||
counter3 = 0
|
||||
counter4 = 0
|
||||
death_counter = 0
|
||||
position = {X=math.random(-2,2),Y=6,Z=math.random(-2,2)}
|
||||
-- This is got in initialization from object_get_base_position(self)
|
||||
position = {X=0,Y=0,Z=0}
|
||||
rotation = {X=0, Y=math.random(0,360), Z=0}
|
||||
dir = 1
|
||||
temp1 = 0
|
||||
|
||||
function step(self, dtime)
|
||||
function on_step(self, dtime)
|
||||
--[[if position.Y > 9.5 then
|
||||
position.Y = 6
|
||||
end
|
||||
if position.Y < 5.5 then
|
||||
position.Y = 9
|
||||
|
||||
counter2 = counter2 - dtime
|
||||
if counter2 < 0 then
|
||||
counter2 = counter2 + 3
|
||||
dir = -dir
|
||||
end]]
|
||||
position.Y = 9]]
|
||||
|
||||
-- Limit step to a sane value; it jumps a lot while the map generator
|
||||
-- is in action
|
||||
|
@ -27,16 +45,19 @@ function step(self, dtime)
|
|||
end
|
||||
|
||||
-- Returned value has these fields:
|
||||
-- * bool walkable
|
||||
-- * int content
|
||||
n = object_get_node(self, position.X,position.Y-0.35,position.Z)
|
||||
if n.walkable then
|
||||
-- * int param1
|
||||
-- * int param2
|
||||
p = {X=position.X, Y=position.Y-0.35, Z=position.Z}
|
||||
n = object_get_node(self, p)
|
||||
f = get_content_features(n.content)
|
||||
if f.walkable then
|
||||
dir = 1
|
||||
else
|
||||
dir = -1
|
||||
end
|
||||
-- Keep the object approximately at ground level
|
||||
position.Y = position.Y + dtime * 1.0 * dir
|
||||
position.Y = position.Y + dtime * 2.0 * dir
|
||||
|
||||
-- Move the object around
|
||||
position.X = position.X + math.cos(math.pi+rotation.Y/180*math.pi)
|
||||
|
@ -46,15 +67,32 @@ function step(self, dtime)
|
|||
|
||||
-- This value has to be set; it determines to which player the
|
||||
-- object is near to and such
|
||||
object_set_base_position(self, position.X,position.Y,position.Z)
|
||||
object_set_base_position(self, position)
|
||||
|
||||
rotation.Y = rotation.Y + dtime * 180
|
||||
counter4 = counter4 - dtime
|
||||
if counter4 < 0 then
|
||||
counter4 = counter4 + math.random(0.5,8)
|
||||
-- Mess around with the map
|
||||
np = vector_add(position, {X=0,Y=0,Z=0})
|
||||
object_place_node(self, np, {content=0})
|
||||
-- A node could be digged out with this:
|
||||
-- object_dig_node(self, np)
|
||||
end
|
||||
|
||||
counter3 = counter3 - dtime
|
||||
if counter3 < 0 then
|
||||
counter3 = counter3 + math.random(1,4)
|
||||
rotation.Y = rotation.Y + math.random(-180, 180)
|
||||
end
|
||||
|
||||
-- Send some custom messages at a custom interval
|
||||
|
||||
counter = counter - dtime
|
||||
if counter < 0 then
|
||||
counter = counter + 0.175
|
||||
counter = counter + 0.25
|
||||
if counter < 0 then
|
||||
counter = 0
|
||||
end
|
||||
|
||||
message = "pos " .. position.X .. " " .. position.Y .. " " .. position.Z
|
||||
object_add_message(self, message)
|
||||
|
@ -63,6 +101,20 @@ function step(self, dtime)
|
|||
object_add_message(self, message)
|
||||
end
|
||||
|
||||
-- Mess around with the map
|
||||
--[[counter2 = counter2 - dtime
|
||||
if counter2 < 0 then
|
||||
counter2 = counter2 + 3
|
||||
if temp1 == 0 then
|
||||
temp1 = 1
|
||||
object_dig_node(self, {X=0,Y=1,Z=0})
|
||||
else
|
||||
temp1 = 0
|
||||
n = {content=1}
|
||||
object_place_node(self, {X=0,Y=5,Z=0}, n)
|
||||
end
|
||||
end]]
|
||||
|
||||
-- Remove the object after some time
|
||||
death_counter = death_counter + dtime
|
||||
if death_counter > 30 then
|
||||
|
@ -72,19 +124,31 @@ function step(self, dtime)
|
|||
end
|
||||
|
||||
-- This stuff is passed to a newly created client-side counterpart of this object
|
||||
function get_client_init_data(self)
|
||||
function on_get_client_init_data(self)
|
||||
-- Just return some data for testing
|
||||
return "result of get_client_init_data"
|
||||
end
|
||||
|
||||
-- This should return some data that mostly saves the state of this object
|
||||
-- Not implemented yet
|
||||
function get_server_init_data(self)
|
||||
-- Not completely implemented yet
|
||||
function on_get_server_init_data(self)
|
||||
-- Just return some data for testing
|
||||
return "result of get_server_init_data"
|
||||
end
|
||||
|
||||
-- At reload time, the output of get_server_init_data is passed to this
|
||||
-- Not implemented yet
|
||||
function initialize(self, data)
|
||||
-- When the object is loaded from scratch, this is called before the first
|
||||
-- on_step(). Data is an empty string or the output of an object spawner
|
||||
-- hook, but such things are not yet implemented.
|
||||
--
|
||||
-- At reload time, the last output of get_server_init_data is passed as data.
|
||||
--
|
||||
-- This should initialize the position of the object to the value returned
|
||||
-- by object_get_base_position(self)
|
||||
--
|
||||
-- Not completely implemented yet
|
||||
--
|
||||
function on_initialize(self, data)
|
||||
print("server object got initialization: " .. data)
|
||||
position = object_get_base_position(self)
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#screenW = 800
|
||||
#screenH = 600
|
||||
#port = 30000
|
||||
#address = kray.dy.fi
|
||||
#address =
|
||||
#name =
|
||||
|
||||
# Whether to try to fog out the border of the visible area
|
||||
|
@ -44,14 +44,14 @@
|
|||
# Server side stuff
|
||||
#
|
||||
|
||||
#map-dir = /home/palle/custom_map
|
||||
# Set to true to enable experimental features
|
||||
#enable_experimental = false
|
||||
|
||||
#plants_amount = 1.0
|
||||
#ravines_amount = 1.0
|
||||
#coal_amount = 1.0
|
||||
#map-dir = /home/palle/custom_map
|
||||
|
||||
# Set to true to enable creative mode (unlimited inventory)
|
||||
#creative_mode = false
|
||||
|
||||
# Player and object positions are sent at intervals specified by this
|
||||
#objectdata_inverval = 0.2
|
||||
|
||||
|
|
|
@ -989,27 +989,14 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
|||
12000 = midday
|
||||
*/
|
||||
{
|
||||
const s32 daylength = 16;
|
||||
const s32 nightlength = 6;
|
||||
const s32 daytimelength = 8;
|
||||
s32 d = daylength;
|
||||
s32 t = (((m_time_of_day.get())%24000)/(24000/d));
|
||||
u32 dr;
|
||||
if(t < nightlength/2 || t >= d - nightlength/2)
|
||||
dr = 300;
|
||||
else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2)
|
||||
dr = 1000;
|
||||
else
|
||||
dr = 750;
|
||||
u32 dr = time_to_daynight_ratio(m_time_of_day.get());
|
||||
|
||||
dstream<<"time_of_day="<<m_time_of_day.get()
|
||||
<<", t="<<t
|
||||
dstream<<"Client: time_of_day="<<m_time_of_day.get()
|
||||
<<", dr="<<dr
|
||||
<<std::endl;
|
||||
|
||||
if(dr != m_env.getDayNightRatio())
|
||||
{
|
||||
//dstream<<"dr="<<dr<<std::endl;
|
||||
dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
|
||||
m_env.setDayNightRatio(dr);
|
||||
m_env.expireMeshes(true);
|
||||
|
@ -1595,7 +1582,7 @@ void Client::addNode(v3s16 p, MapNode n)
|
|||
|
||||
void Client::updateCamera(v3f pos, v3f dir)
|
||||
{
|
||||
m_env.getMap().updateCamera(pos, dir);
|
||||
m_env.getClientMap().updateCamera(pos, dir);
|
||||
camera_position = pos;
|
||||
camera_direction = dir;
|
||||
}
|
||||
|
|
|
@ -175,35 +175,36 @@ extern "C"{
|
|||
}
|
||||
|
||||
/*
|
||||
Functions for calling from script:
|
||||
|
||||
object_set_position(self, x, y, z)
|
||||
object_set_rotation(self, x, y, z)
|
||||
object_add_to_mesh(self, image, corners, backface_culling)
|
||||
object_clear_mesh(self)
|
||||
|
||||
Callbacks in script:
|
||||
|
||||
step(self, dtime)
|
||||
process_message(self, data)
|
||||
initialize(self, data)
|
||||
on_step(self, dtime)
|
||||
on_process_message(self, data)
|
||||
on_initialize(self, data)
|
||||
TODO:
|
||||
string status_text(self)
|
||||
string on_get_info_text(self)
|
||||
on_block_removed_near({X=,Y=,Z=}, node)
|
||||
on_block_placed_near({X=,Y=,Z=}, node)
|
||||
*/
|
||||
|
||||
/*
|
||||
object_set_position(self, x, y, z)
|
||||
object_set_position(self, p)
|
||||
*/
|
||||
static int lf_object_set_position(lua_State *L)
|
||||
{
|
||||
// 4: z
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 3: y
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 2: x
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
|
||||
|
@ -217,18 +218,24 @@ static int lf_object_set_position(lua_State *L)
|
|||
}
|
||||
|
||||
/*
|
||||
object_set_rotation(self, x, y, z)
|
||||
object_set_rotation(self, p)
|
||||
*/
|
||||
static int lf_object_set_rotation(lua_State *L)
|
||||
{
|
||||
// 4: z
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 3: y
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 2: x
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
|
||||
|
@ -381,7 +388,7 @@ void LuaCAO::step(float dtime)
|
|||
Call step(self, dtime) from lua
|
||||
*/
|
||||
|
||||
const char *funcname = "step";
|
||||
const char *funcname = "on_step";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
@ -413,7 +420,7 @@ void LuaCAO::processMessage(const std::string &data)
|
|||
Call process_message(self, data) from lua
|
||||
*/
|
||||
|
||||
const char *funcname = "process_message";
|
||||
const char *funcname = "on_process_message";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
@ -462,7 +469,7 @@ void LuaCAO::initialize(const std::string &data)
|
|||
Call initialize(self, data) in the script
|
||||
*/
|
||||
|
||||
const char *funcname = "initialize";
|
||||
const char *funcname = "on_initialize";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
|
|
@ -51,7 +51,9 @@ void set_default_settings()
|
|||
g_settings.setDefault("fast_move", "false");
|
||||
|
||||
// Server stuff
|
||||
g_settings.setDefault("creative_mode", "false");
|
||||
g_settings.setDefault("fast_move", "false");
|
||||
|
||||
g_settings.setDefault("enable_experimental", "false");
|
||||
|
||||
g_settings.setDefault("objectdata_interval", "0.2");
|
||||
g_settings.setDefault("active_object_range", "2");
|
||||
|
|
|
@ -96,6 +96,25 @@ Player * Environment::getPlayer(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Player * Environment::getRandomConnectedPlayer()
|
||||
{
|
||||
core::list<Player*> connected_players = getPlayers(true);
|
||||
u32 chosen_one = myrand() % connected_players.size();
|
||||
u32 j = 0;
|
||||
for(core::list<Player*>::Iterator
|
||||
i = connected_players.begin();
|
||||
i != connected_players.end(); i++)
|
||||
{
|
||||
if(j == chosen_one)
|
||||
{
|
||||
Player *player = *i;
|
||||
return player;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
core::list<Player*> Environment::getPlayers()
|
||||
{
|
||||
return m_players;
|
||||
|
@ -147,8 +166,9 @@ u32 Environment::getDayNightRatio()
|
|||
ServerEnvironment
|
||||
*/
|
||||
|
||||
ServerEnvironment::ServerEnvironment(ServerMap *map):
|
||||
ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
|
||||
m_map(map),
|
||||
m_server(server),
|
||||
m_random_spawn_timer(0)
|
||||
{
|
||||
}
|
||||
|
@ -382,6 +402,9 @@ void ServerEnvironment::step(float dtime)
|
|||
}
|
||||
}
|
||||
|
||||
if(g_settings.getBool("enable_experimental"))
|
||||
{
|
||||
|
||||
/*
|
||||
Step active objects
|
||||
*/
|
||||
|
@ -445,19 +468,36 @@ void ServerEnvironment::step(float dtime)
|
|||
v3f(myrand_range(-2*BS,2*BS), BS*5, myrand_range(-2*BS,2*BS)));*/
|
||||
|
||||
/*
|
||||
Create a Lua ServerActiveObject somewhere near the origin
|
||||
Find some position
|
||||
*/
|
||||
LuaSAO *obj = new LuaSAO(this, 0,
|
||||
v3f(myrand_range(-2*BS,2*BS),
|
||||
myrand_range(2*BS,9*BS),
|
||||
myrand_range(-2*BS,2*BS))
|
||||
|
||||
/*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
|
||||
s16 y = 1 + getServerMap().findGroundLevel(p2d);
|
||||
v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
|
||||
|
||||
Player *player = getRandomConnectedPlayer();
|
||||
v3f pos(0,0,0);
|
||||
if(player)
|
||||
pos = player->getPosition();
|
||||
pos += v3f(
|
||||
myrand_range(-5,5)*BS,
|
||||
0,
|
||||
myrand_range(-5,5)*BS
|
||||
);
|
||||
|
||||
/*
|
||||
Create a LuaSAO (ServerActiveObject)
|
||||
*/
|
||||
|
||||
LuaSAO *obj = new LuaSAO(this, 0, pos);
|
||||
|
||||
/*
|
||||
Select a random type for it
|
||||
*/
|
||||
std::string objectdir = porting::getDataPath("luaobjects");
|
||||
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(objectdir);
|
||||
if(dirlist.size() > 0)
|
||||
{
|
||||
u32 selected_i = myrand_range(0, dirlist.size()-1);
|
||||
std::string selected_name = "";
|
||||
selected_name = dirlist[selected_i].name;
|
||||
|
@ -469,11 +509,14 @@ void ServerEnvironment::step(float dtime)
|
|||
/*
|
||||
Load the scripts for the type
|
||||
*/
|
||||
obj->loadScripts(selected_name.c_str());
|
||||
obj->initializeFromNothing(selected_name.c_str());
|
||||
|
||||
// Add the object to the environment
|
||||
addActiveObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
} // enable_experimental
|
||||
}
|
||||
|
||||
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
void removePlayer(u16 peer_id);
|
||||
Player * getPlayer(u16 peer_id);
|
||||
Player * getPlayer(const char *name);
|
||||
Player * getRandomConnectedPlayer();
|
||||
core::list<Player*> getPlayers();
|
||||
core::list<Player*> getPlayers(bool ignore_disconnected);
|
||||
void printPlayers(std::ostream &o);
|
||||
|
@ -79,10 +80,12 @@ protected:
|
|||
|
||||
#include "serverobject.h"
|
||||
|
||||
class Server;
|
||||
|
||||
class ServerEnvironment : public Environment
|
||||
{
|
||||
public:
|
||||
ServerEnvironment(ServerMap *map);
|
||||
ServerEnvironment(ServerMap *map, Server *server);
|
||||
~ServerEnvironment();
|
||||
|
||||
Map & getMap()
|
||||
|
@ -95,6 +98,11 @@ public:
|
|||
return *m_map;
|
||||
}
|
||||
|
||||
Server * getServer()
|
||||
{
|
||||
return m_server;
|
||||
}
|
||||
|
||||
void step(f32 dtime);
|
||||
|
||||
void serializePlayers(const std::string &savedir);
|
||||
|
@ -140,6 +148,7 @@ public:
|
|||
|
||||
private:
|
||||
ServerMap *m_map;
|
||||
Server *m_server;
|
||||
core::map<u16, ServerActiveObject*> m_active_objects;
|
||||
Queue<ActiveObjectMessage> m_active_object_messages;
|
||||
float m_random_spawn_timer;
|
||||
|
|
27
src/main.cpp
27
src/main.cpp
|
@ -199,15 +199,6 @@ FIXME: Server went into some infinite PeerNotFoundException loop
|
|||
Objects:
|
||||
--------
|
||||
|
||||
TODO: Better handling of objects and mobs
|
||||
- Scripting?
|
||||
- There has to be some way to do it with less messy code
|
||||
- Make separate classes for client and server
|
||||
- Client should not discriminate between blocks, server should
|
||||
- Make other players utilize the same framework
|
||||
- This is also needed for objects that don't get sent to client
|
||||
but are used for triggers etc
|
||||
|
||||
TODO: There has to be some better way to handle static objects than to
|
||||
send them all the time. This affects signs and item objects.
|
||||
SUGG: Signs could be done in the same way as torches. For this, blocks
|
||||
|
@ -262,6 +253,7 @@ Doing now (most important at the top):
|
|||
* not done
|
||||
|
||||
=== Fixmes
|
||||
* Check the fixmes in the list above
|
||||
* Make server find the spawning place from the real map data, not from
|
||||
the heightmap
|
||||
- But the changing borders of chunk have to be avoided, because
|
||||
|
@ -269,13 +261,16 @@ Doing now (most important at the top):
|
|||
* Make the generator to run in background and not blocking block
|
||||
placement and transfer
|
||||
* only_from_disk might not work anymore - check and fix it.
|
||||
* Check the fixmes in the list above
|
||||
|
||||
=== Making it more portable
|
||||
* Some MSVC: std::sto* are defined without a namespace and collide
|
||||
with the ones in utility.h
|
||||
|
||||
=== Features
|
||||
* Map should make the appropriate MapEditEvents
|
||||
* Add a global Lua spawn handler and such
|
||||
* Get rid of MapBlockObjects
|
||||
* Other players could be sent to clients as LuaCAOs
|
||||
* Add mud underground
|
||||
* Make an "environment metafile" to store at least time of day
|
||||
* Move digging property stuff from material.{h,cpp} to mapnode.cpp...
|
||||
|
@ -283,17 +278,15 @@ Doing now (most important at the top):
|
|||
* Add some kind of erosion and other stuff that now is possible
|
||||
* Make client to fetch stuff asynchronously
|
||||
- Needs method SyncProcessData
|
||||
* Fix the problem with the server constantly saving one or a few
|
||||
blocks? List the first saved block, maybe it explains.
|
||||
- It is probably caused by oscillating water
|
||||
* Water doesn't start flowing after map generation like it should
|
||||
- Are there still problems?
|
||||
* Better water generation (spread it to underwater caverns but don't
|
||||
fill dungeons that don't touch big water masses)
|
||||
* When generating a chunk and the neighboring chunk doesn't have mud
|
||||
and stuff yet and the ground is fairly flat, the mud will flow to
|
||||
the other chunk making nasty straight walls when the other chunk
|
||||
is generated. Fix it.
|
||||
* Fix the problem with the server constantly saving one or a few
|
||||
blocks? List the first saved block, maybe it explains.
|
||||
- It is probably caused by oscillating water
|
||||
* Make a small history check to transformLiquids to detect and log
|
||||
continuous oscillations, in such detail that they can be fixed.
|
||||
* Combine meshes to bigger ones in ClientMap and set them EHM_STATIC
|
||||
|
@ -1796,7 +1789,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
dstream<<"Created main menu"<<std::endl;
|
||||
|
||||
while(g_device->run())
|
||||
while(g_device->run() && kill == false)
|
||||
{
|
||||
// Run global IrrlichtWrapper's main thread processing stuff
|
||||
g_irrlicht->Run();
|
||||
|
@ -1811,7 +1804,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if(g_device->run() == false)
|
||||
if(g_device->run() == false || kill == true)
|
||||
break;
|
||||
|
||||
dstream<<"Dropping main menu"<<std::endl;
|
||||
|
|
202
src/map.cpp
202
src/map.cpp
|
@ -34,30 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
Map::Map(std::ostream &dout):
|
||||
m_dout(dout),
|
||||
m_camera_position(0,0,0),
|
||||
m_camera_direction(0,0,1),
|
||||
m_sector_cache(NULL)
|
||||
{
|
||||
m_sector_mutex.Init();
|
||||
m_camera_mutex.Init();
|
||||
assert(m_sector_mutex.IsInitialized());
|
||||
assert(m_camera_mutex.IsInitialized());
|
||||
|
||||
// Get this so that the player can stay on it at first
|
||||
//getSector(v2s16(0,0));
|
||||
}
|
||||
|
||||
Map::~Map()
|
||||
{
|
||||
/*
|
||||
Stop updater thread
|
||||
*/
|
||||
/*updater.setRun(false);
|
||||
while(updater.IsRunning())
|
||||
sleep_s(1);*/
|
||||
|
||||
/*
|
||||
Free all MapSectors.
|
||||
Free all MapSectors
|
||||
*/
|
||||
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
|
||||
for(; i.atEnd() == false; i++)
|
||||
|
@ -67,6 +53,29 @@ Map::~Map()
|
|||
}
|
||||
}
|
||||
|
||||
void Map::addEventReceiver(MapEventReceiver *event_receiver)
|
||||
{
|
||||
m_event_receivers.insert(event_receiver, false);
|
||||
}
|
||||
|
||||
void Map::removeEventReceiver(MapEventReceiver *event_receiver)
|
||||
{
|
||||
if(m_event_receivers.find(event_receiver) == NULL)
|
||||
return;
|
||||
m_event_receivers.remove(event_receiver);
|
||||
}
|
||||
|
||||
void Map::dispatchEvent(MapEditEvent *event)
|
||||
{
|
||||
for(core::map<MapEventReceiver*, bool>::Iterator
|
||||
i = m_event_receivers.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
MapEventReceiver* event_receiver = i.getNode()->getKey();
|
||||
event_receiver->onMapEditEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
|
||||
{
|
||||
if(m_sector_cache != NULL && p == m_sector_cache_p){
|
||||
|
@ -145,34 +154,6 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
|
|||
return block;
|
||||
}*/
|
||||
|
||||
f32 Map::getGroundHeight(v2s16 p, bool generate)
|
||||
{
|
||||
try{
|
||||
v2s16 sectorpos = getNodeSectorPos(p);
|
||||
MapSector * sref = getSectorNoGenerate(sectorpos);
|
||||
v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
|
||||
f32 y = sref->getGroundHeight(relpos);
|
||||
return y;
|
||||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
|
||||
{
|
||||
/*m_dout<<DTIME<<"Map::setGroundHeight(("
|
||||
<<p.X<<","<<p.Y
|
||||
<<"), "<<y<<")"<<std::endl;*/
|
||||
v2s16 sectorpos = getNodeSectorPos(p);
|
||||
MapSector * sref = getSectorNoGenerate(sectorpos);
|
||||
v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
|
||||
//sref->mutex.Lock();
|
||||
sref->setGroundHeight(relpos, y);
|
||||
//sref->mutex.Unlock();
|
||||
}
|
||||
|
||||
bool Map::isNodeUnderground(v3s16 p)
|
||||
{
|
||||
v3s16 blockpos = getNodeBlockPos(p);
|
||||
|
@ -947,8 +928,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
|||
assert(block != NULL);
|
||||
modified_blocks.insert(blockpos, block);
|
||||
|
||||
if(isValidPosition(p) == false)
|
||||
throw;
|
||||
assert(isValidPosition(p));
|
||||
|
||||
// Unlight neighbours of node.
|
||||
// This means setting light of all consequent dimmer nodes
|
||||
|
@ -1161,7 +1141,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
|
|||
}
|
||||
catch(InvalidPositionException &e)
|
||||
{
|
||||
throw;
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1221,6 +1201,63 @@ void Map::removeNodeAndUpdate(v3s16 p,
|
|||
}
|
||||
}
|
||||
|
||||
bool Map::addNodeWithEvent(v3s16 p, MapNode n)
|
||||
{
|
||||
MapEditEvent event;
|
||||
event.type = MEET_ADDNODE;
|
||||
event.p = p;
|
||||
event.n = n;
|
||||
|
||||
bool succeeded = true;
|
||||
try{
|
||||
core::map<v3s16, MapBlock*> modified_blocks;
|
||||
addNodeAndUpdate(p, n, modified_blocks);
|
||||
|
||||
// Copy modified_blocks to event
|
||||
for(core::map<v3s16, MapBlock*>::Iterator
|
||||
i = modified_blocks.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
event.modified_blocks.insert(i.getNode()->getKey(), false);
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e){
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
dispatchEvent(&event);
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool Map::removeNodeWithEvent(v3s16 p)
|
||||
{
|
||||
MapEditEvent event;
|
||||
event.type = MEET_REMOVENODE;
|
||||
event.p = p;
|
||||
|
||||
bool succeeded = true;
|
||||
try{
|
||||
core::map<v3s16, MapBlock*> modified_blocks;
|
||||
removeNodeAndUpdate(p, modified_blocks);
|
||||
|
||||
// Copy modified_blocks to event
|
||||
for(core::map<v3s16, MapBlock*>::Iterator
|
||||
i = modified_blocks.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
event.modified_blocks.insert(i.getNode()->getKey(), false);
|
||||
}
|
||||
}
|
||||
catch(InvalidPositionException &e){
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
dispatchEvent(&event);
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool Map::dayNightDiffed(v3s16 blockpos)
|
||||
{
|
||||
try{
|
||||
|
@ -4535,6 +4572,42 @@ MapBlock * ServerMap::emergeBlock(
|
|||
return block;
|
||||
}
|
||||
|
||||
s16 ServerMap::findGroundLevel(v2s16 p2d)
|
||||
{
|
||||
/*
|
||||
Uh, just do something random...
|
||||
*/
|
||||
// Find existing map from top to down
|
||||
s16 max=63;
|
||||
s16 min=-64;
|
||||
v3s16 p(p2d.X, max, p2d.Y);
|
||||
for(; p.Y>min; p.Y--)
|
||||
{
|
||||
MapNode n = getNodeNoEx(p);
|
||||
if(n.d != CONTENT_IGNORE)
|
||||
break;
|
||||
}
|
||||
if(p.Y == min)
|
||||
goto plan_b;
|
||||
// If this node is not air, go to plan b
|
||||
if(getNodeNoEx(p).d != CONTENT_AIR)
|
||||
goto plan_b;
|
||||
// Search existing walkable and return it
|
||||
for(; p.Y>min; p.Y--)
|
||||
{
|
||||
MapNode n = getNodeNoEx(p);
|
||||
if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
|
||||
return p.Y;
|
||||
}
|
||||
// Move to plan b
|
||||
plan_b:
|
||||
/*
|
||||
Plan B: Get from map generator perlin noise function
|
||||
*/
|
||||
double level = base_rock_level_2d(m_seed, p2d);
|
||||
return (s16)level;
|
||||
}
|
||||
|
||||
void ServerMap::createDir(std::string path)
|
||||
{
|
||||
if(fs::CreateDir(path) == false)
|
||||
|
@ -5122,28 +5195,6 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
|
|||
}
|
||||
}
|
||||
|
||||
// Gets from master heightmap
|
||||
void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
|
||||
{
|
||||
dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
|
||||
//assert(m_heightmap != NULL);
|
||||
/*
|
||||
Corner definition:
|
||||
v2s16(0,0),
|
||||
v2s16(1,0),
|
||||
v2s16(1,1),
|
||||
v2s16(0,1),
|
||||
*/
|
||||
/*corners[0] = m_heightmap->getGroundHeight
|
||||
((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
|
||||
corners[1] = m_heightmap->getGroundHeight
|
||||
((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
|
||||
corners[2] = m_heightmap->getGroundHeight
|
||||
((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
|
||||
corners[3] = m_heightmap->getGroundHeight
|
||||
((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
|
||||
}
|
||||
|
||||
void ServerMap::PrintInfo(std::ostream &out)
|
||||
{
|
||||
out<<"ServerMap: ";
|
||||
|
@ -5165,20 +5216,15 @@ ClientMap::ClientMap(
|
|||
Map(dout_client),
|
||||
scene::ISceneNode(parent, mgr, id),
|
||||
m_client(client),
|
||||
m_control(control)
|
||||
m_control(control),
|
||||
m_camera_position(0,0,0),
|
||||
m_camera_direction(0,0,1)
|
||||
{
|
||||
//mesh_mutex.Init();
|
||||
m_camera_mutex.Init();
|
||||
assert(m_camera_mutex.IsInitialized());
|
||||
|
||||
/*m_box = core::aabbox3d<f32>(0,0,0,
|
||||
map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
|
||||
/*m_box = core::aabbox3d<f32>(0,0,0,
|
||||
map->getSizeNodes().X * BS,
|
||||
map->getSizeNodes().Y * BS,
|
||||
map->getSizeNodes().Z * BS);*/
|
||||
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
|
||||
BS*1000000,BS*1000000,BS*1000000);
|
||||
|
||||
//setPosition(v3f(BS,BS,BS));
|
||||
}
|
||||
|
||||
ClientMap::~ClientMap()
|
||||
|
|
105
src/map.h
105
src/map.h
|
@ -44,6 +44,51 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#define MAPTYPE_SERVER 1
|
||||
#define MAPTYPE_CLIENT 2
|
||||
|
||||
enum MapEditEventType{
|
||||
MEET_ADDNODE,
|
||||
MEET_REMOVENODE,
|
||||
MEET_OTHER
|
||||
};
|
||||
|
||||
struct MapEditEvent
|
||||
{
|
||||
MapEditEventType type;
|
||||
v3s16 p;
|
||||
MapNode n;
|
||||
core::map<v3s16, bool> modified_blocks;
|
||||
u16 already_known_by_peer;
|
||||
|
||||
MapEditEvent():
|
||||
type(MEET_OTHER),
|
||||
already_known_by_peer(0)
|
||||
{
|
||||
}
|
||||
|
||||
MapEditEvent * clone()
|
||||
{
|
||||
MapEditEvent *event = new MapEditEvent();
|
||||
event->type = type;
|
||||
event->p = p;
|
||||
event->n = n;
|
||||
for(core::map<v3s16, bool>::Iterator
|
||||
i = modified_blocks.getIterator();
|
||||
i.atEnd()==false; i++)
|
||||
{
|
||||
v3s16 p = i.getNode()->getKey();
|
||||
bool v = i.getNode()->getValue();
|
||||
event->modified_blocks.insert(p, v);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
};
|
||||
|
||||
class MapEventReceiver
|
||||
{
|
||||
public:
|
||||
// event shall be deleted by caller after the call.
|
||||
virtual void onMapEditEvent(MapEditEvent *event) = 0;
|
||||
};
|
||||
|
||||
class Map : public NodeContainer
|
||||
{
|
||||
public:
|
||||
|
@ -69,24 +114,10 @@ public:
|
|||
delete this;
|
||||
}
|
||||
|
||||
void updateCamera(v3f pos, v3f dir)
|
||||
{
|
||||
JMutexAutoLock lock(m_camera_mutex);
|
||||
m_camera_position = pos;
|
||||
m_camera_direction = dir;
|
||||
}
|
||||
|
||||
static core::aabbox3d<f32> getNodeBox(v3s16 p)
|
||||
{
|
||||
return core::aabbox3d<f32>(
|
||||
(float)p.X * BS - 0.5*BS,
|
||||
(float)p.Y * BS - 0.5*BS,
|
||||
(float)p.Z * BS - 0.5*BS,
|
||||
(float)p.X * BS + 0.5*BS,
|
||||
(float)p.Y * BS + 0.5*BS,
|
||||
(float)p.Z * BS + 0.5*BS
|
||||
);
|
||||
}
|
||||
void addEventReceiver(MapEventReceiver *event_receiver);
|
||||
void removeEventReceiver(MapEventReceiver *event_receiver);
|
||||
// event shall be deleted by caller after the call.
|
||||
void dispatchEvent(MapEditEvent *event);
|
||||
|
||||
// On failure returns NULL
|
||||
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
|
||||
|
@ -112,10 +143,6 @@ public:
|
|||
// Gets an existing block or creates an empty one
|
||||
//MapBlock * getBlockCreate(v3s16 p);
|
||||
|
||||
// Returns InvalidPositionException if not found
|
||||
f32 getGroundHeight(v2s16 p, bool generate=false);
|
||||
void setGroundHeight(v2s16 p, f32 y, bool generate=false);
|
||||
|
||||
// Returns InvalidPositionException if not found
|
||||
bool isNodeUnderground(v3s16 p);
|
||||
|
||||
|
@ -212,6 +239,14 @@ public:
|
|||
void removeNodeAndUpdate(v3s16 p,
|
||||
core::map<v3s16, MapBlock*> &modified_blocks);
|
||||
|
||||
/*
|
||||
Wrappers for the latter ones.
|
||||
These emit events.
|
||||
Return true if succeeded, false if not.
|
||||
*/
|
||||
bool addNodeWithEvent(v3s16 p, MapNode n);
|
||||
bool removeNodeWithEvent(v3s16 p);
|
||||
|
||||
/*
|
||||
Takes the blocks at the edges into account
|
||||
*/
|
||||
|
@ -249,13 +284,12 @@ protected:
|
|||
|
||||
std::ostream &m_dout;
|
||||
|
||||
core::map<MapEventReceiver*, bool> m_event_receivers;
|
||||
|
||||
// Mutex is important because on client map is accessed asynchronously
|
||||
core::map<v2s16, MapSector*> m_sectors;
|
||||
JMutex m_sector_mutex;
|
||||
|
||||
v3f m_camera_position;
|
||||
v3f m_camera_direction;
|
||||
JMutex m_camera_mutex;
|
||||
|
||||
// Be sure to set this to NULL when the cached sector is deleted
|
||||
MapSector *m_sector_cache;
|
||||
v2s16 m_sector_cache_p;
|
||||
|
@ -445,6 +479,9 @@ public:
|
|||
);
|
||||
#endif
|
||||
|
||||
// Helper for placing objects on ground level
|
||||
s16 findGroundLevel(v2s16 p2d);
|
||||
|
||||
/*
|
||||
Misc. helper functions for fiddling with directory and file
|
||||
names when saving
|
||||
|
@ -489,10 +526,6 @@ public:
|
|||
// This will generate a sector with getSector if not found.
|
||||
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
|
||||
|
||||
// Gets from master heightmap
|
||||
// DEPRECATED?
|
||||
void getSectorCorners(v2s16 p2d, s16 *corners);
|
||||
|
||||
// For debug printing
|
||||
virtual void PrintInfo(std::ostream &out);
|
||||
|
||||
|
@ -573,6 +606,13 @@ public:
|
|||
ISceneNode::drop();
|
||||
}
|
||||
|
||||
void updateCamera(v3f pos, v3f dir)
|
||||
{
|
||||
JMutexAutoLock lock(m_camera_mutex);
|
||||
m_camera_position = pos;
|
||||
m_camera_direction = dir;
|
||||
}
|
||||
|
||||
/*
|
||||
Forcefully get a sector from somewhere
|
||||
*/
|
||||
|
@ -640,6 +680,11 @@ private:
|
|||
//JMutex mesh_mutex;
|
||||
|
||||
MapDrawControl &m_control;
|
||||
|
||||
v3f m_camera_position;
|
||||
v3f m_camera_direction;
|
||||
JMutex m_camera_mutex;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,10 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include "mapblockobject.h"
|
||||
#include "mapblock.h"
|
||||
// Only for ::getNodeBox, TODO: Get rid of this
|
||||
// For object wrapping
|
||||
#include "map.h"
|
||||
#include "inventory.h"
|
||||
#include "irrlichtwrapper.h"
|
||||
#include "utility.h"
|
||||
|
||||
/*
|
||||
MapBlockObject
|
||||
|
@ -168,8 +169,7 @@ void MovingObject::move(float dtime, v3f acceleration)
|
|||
// walking over map borders
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> nodebox = Map::getNodeBox(
|
||||
v3s16(x,y,z));
|
||||
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
|
||||
|
||||
// See if the object is touching ground
|
||||
if(
|
||||
|
|
|
@ -428,8 +428,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
|
|||
// walking over map borders
|
||||
}
|
||||
|
||||
core::aabbox3d<f32> nodebox = Map::getNodeBox(
|
||||
v3s16(x,y,z));
|
||||
core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
|
||||
|
||||
/*
|
||||
See if the player is touching ground.
|
||||
|
|
|
@ -51,7 +51,7 @@ void sigint_handler(int sig)
|
|||
dstream<<DTIME<<"INFO: sigint_handler(): "
|
||||
<<"Ctrl-C pressed, shutting down."<<std::endl;
|
||||
|
||||
dstream<<DTIME<<"INFO: siging_handler(): "
|
||||
dstream<<DTIME<<"INFO: sigint_handler(): "
|
||||
<<"Printing debug stacks"<<std::endl;
|
||||
debug_stacks_print();
|
||||
|
||||
|
|
287
src/server.cpp
287
src/server.cpp
|
@ -893,7 +893,7 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
|
|||
Server::Server(
|
||||
std::string mapsavedir
|
||||
):
|
||||
m_env(new ServerMap(mapsavedir)),
|
||||
m_env(new ServerMap(mapsavedir), this),
|
||||
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
|
||||
m_thread(this),
|
||||
m_emergethread(this),
|
||||
|
@ -902,9 +902,10 @@ Server::Server(
|
|||
m_time_of_day_send_timer(0),
|
||||
m_uptime(0),
|
||||
m_mapsavedir(mapsavedir),
|
||||
m_shutdown_requested(false)
|
||||
m_shutdown_requested(false),
|
||||
m_ignore_map_edit_events(false),
|
||||
m_ignore_map_edit_events_peer_id(0)
|
||||
{
|
||||
//m_flowwater_timer = 0.0;
|
||||
m_liquid_transform_timer = 0.0;
|
||||
m_print_info_timer = 0.0;
|
||||
m_objectdata_timer = 0.0;
|
||||
|
@ -916,6 +917,8 @@ Server::Server(
|
|||
m_step_dtime_mutex.Init();
|
||||
m_step_dtime = 0.0;
|
||||
|
||||
m_env.getMap().addEventReceiver(this);
|
||||
|
||||
// Load players
|
||||
m_env.deSerializePlayers(m_mapsavedir);
|
||||
}
|
||||
|
@ -1191,6 +1194,9 @@ void Server::AsyncRunStep()
|
|||
}
|
||||
}
|
||||
|
||||
if(g_settings.getBool("enable_experimental"))
|
||||
{
|
||||
|
||||
/*
|
||||
Check added and deleted active objects
|
||||
*/
|
||||
|
@ -1198,14 +1204,18 @@ void Server::AsyncRunStep()
|
|||
JMutexAutoLock envlock(m_env_mutex);
|
||||
JMutexAutoLock conlock(m_con_mutex);
|
||||
|
||||
// Radius inside which objects are active
|
||||
s16 radius = 32;
|
||||
|
||||
for(core::map<u16, RemoteClient*>::Iterator
|
||||
i = m_clients.getIterator();
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
RemoteClient *client = i.getNode()->getValue();
|
||||
Player *player = m_env.getPlayer(client->peer_id);
|
||||
if(player==NULL)
|
||||
continue;
|
||||
v3s16 pos = floatToInt(player->getPosition(), BS);
|
||||
s16 radius = 32;
|
||||
|
||||
core::map<u16, bool> removed_objects;
|
||||
core::map<u16, bool> added_objects;
|
||||
|
@ -1407,8 +1417,44 @@ void Server::AsyncRunStep()
|
|||
}
|
||||
}
|
||||
|
||||
} // enable_experimental
|
||||
|
||||
/*
|
||||
Send queued-for-sending map edit events.
|
||||
*/
|
||||
{
|
||||
while(m_unsent_map_edit_queue.size() != 0)
|
||||
{
|
||||
MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
|
||||
|
||||
if(event->type == MEET_ADDNODE)
|
||||
{
|
||||
dstream<<"Server: MEET_ADDNODE"<<std::endl;
|
||||
sendAddNode(event->p, event->n, event->already_known_by_peer);
|
||||
}
|
||||
else if(event->type == MEET_REMOVENODE)
|
||||
{
|
||||
dstream<<"Server: MEET_REMOVENODE"<<std::endl;
|
||||
sendRemoveNode(event->p, event->already_known_by_peer);
|
||||
}
|
||||
else if(event->type == MEET_OTHER)
|
||||
{
|
||||
dstream<<"WARNING: Server: MEET_OTHER not implemented"
|
||||
<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
dstream<<"WARNING: Server: Unknown MapEditEvent "
|
||||
<<((u32)event->type)<<std::endl;
|
||||
}
|
||||
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Send object positions
|
||||
TODO: Get rid of MapBlockObjects
|
||||
*/
|
||||
{
|
||||
float &counter = m_objectdata_timer;
|
||||
|
@ -1964,32 +2010,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||
/*
|
||||
Send the removal to all other clients
|
||||
*/
|
||||
|
||||
// Create packet
|
||||
u32 replysize = 8;
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
writeU16(&reply[0], TOCLIENT_REMOVENODE);
|
||||
writeS16(&reply[2], p_under.X);
|
||||
writeS16(&reply[4], p_under.Y);
|
||||
writeS16(&reply[6], p_under.Z);
|
||||
|
||||
for(core::map<u16, RemoteClient*>::Iterator
|
||||
i = m_clients.getIterator();
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
// Get client and check that it is valid
|
||||
RemoteClient *client = i.getNode()->getValue();
|
||||
assert(client->peer_id == i.getNode()->getKey());
|
||||
if(client->serialization_version == SER_FMT_VER_INVALID)
|
||||
continue;
|
||||
|
||||
// Don't send if it's the same one
|
||||
if(peer_id == client->peer_id)
|
||||
continue;
|
||||
|
||||
// Send as reliable
|
||||
m_con.Send(client->peer_id, 0, reply, true);
|
||||
}
|
||||
sendRemoveNode(p_over, peer_id);
|
||||
|
||||
/*
|
||||
Update and send inventory
|
||||
|
@ -2063,33 +2084,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||
Remove the node
|
||||
(this takes some time so it is done after the quick stuff)
|
||||
*/
|
||||
m_ignore_map_edit_events = true;
|
||||
m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
|
||||
|
||||
#if 0
|
||||
/*
|
||||
Update water
|
||||
*/
|
||||
|
||||
// Update water pressure around modification
|
||||
// This also adds it to m_flow_active_nodes if appropriate
|
||||
|
||||
MapVoxelManipulator v(&m_env.getMap());
|
||||
v.m_disable_water_climb =
|
||||
g_settings.getBool("disable_water_climb");
|
||||
|
||||
VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
|
||||
|
||||
try
|
||||
{
|
||||
v.updateAreaWaterPressure(area, m_flow_active_nodes);
|
||||
}
|
||||
catch(ProcessingLimitException &e)
|
||||
{
|
||||
dstream<<"Processing limit reached (1)"<<std::endl;
|
||||
}
|
||||
|
||||
v.blitBack(modified_blocks);
|
||||
#endif
|
||||
m_ignore_map_edit_events = false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2151,16 +2148,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||
if(content_features(n.d).wall_mounted)
|
||||
n.dir = packDir(p_under - p_over);
|
||||
|
||||
// Create packet
|
||||
u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver);
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
writeU16(&reply[0], TOCLIENT_ADDNODE);
|
||||
writeS16(&reply[2], p_over.X);
|
||||
writeS16(&reply[4], p_over.Y);
|
||||
writeS16(&reply[6], p_over.Z);
|
||||
n.serialize(&reply[8], peer_ser_ver);
|
||||
// Send as reliable
|
||||
m_con.SendToAll(0, reply, true);
|
||||
/*
|
||||
Send to all players
|
||||
*/
|
||||
sendAddNode(p_over, n, 0);
|
||||
|
||||
/*
|
||||
Handle inventory
|
||||
|
@ -2183,7 +2174,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||
This takes some time so it is done after the quick stuff
|
||||
*/
|
||||
core::map<v3s16, MapBlock*> modified_blocks;
|
||||
m_ignore_map_edit_events = true;
|
||||
m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
|
||||
m_ignore_map_edit_events = false;
|
||||
|
||||
/*
|
||||
Calculate special events
|
||||
|
@ -2595,41 +2588,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||
}
|
||||
}
|
||||
|
||||
/*void Server::Send(u16 peer_id, u16 channelnum,
|
||||
SharedBuffer<u8> data, bool reliable)
|
||||
void Server::onMapEditEvent(MapEditEvent *event)
|
||||
{
|
||||
JMutexAutoLock lock(m_con_mutex);
|
||||
m_con.Send(peer_id, channelnum, data, reliable);
|
||||
}*/
|
||||
|
||||
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
/*
|
||||
Create a packet with the block in the right format
|
||||
*/
|
||||
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
block->serialize(os, ver);
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
|
||||
|
||||
u32 replysize = 8 + blockdata.getSize();
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
v3s16 p = block->getPos();
|
||||
writeU16(&reply[0], TOCLIENT_BLOCKDATA);
|
||||
writeS16(&reply[2], p.X);
|
||||
writeS16(&reply[4], p.Y);
|
||||
writeS16(&reply[6], p.Z);
|
||||
memcpy(&reply[8], *blockdata, blockdata.getSize());
|
||||
|
||||
/*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
|
||||
<<": \tpacket size: "<<replysize<<std::endl;*/
|
||||
|
||||
/*
|
||||
Send packet
|
||||
*/
|
||||
m_con.Send(peer_id, 1, reply, true);
|
||||
dstream<<"Server::onMapEditEvent()"<<std::endl;
|
||||
if(m_ignore_map_edit_events)
|
||||
return;
|
||||
MapEditEvent *e = event->clone();
|
||||
m_unsent_map_edit_queue.push_back(e);
|
||||
}
|
||||
|
||||
core::list<PlayerInfo> Server::getPlayerInfo()
|
||||
|
@ -2759,6 +2724,10 @@ void Server::SendPlayerInfos()
|
|||
m_con.SendToAll(0, data, true);
|
||||
}
|
||||
|
||||
/*
|
||||
Craft checking system
|
||||
*/
|
||||
|
||||
enum ItemSpecType
|
||||
{
|
||||
ITEM_NONE,
|
||||
|
@ -3109,6 +3078,97 @@ void Server::BroadcastChatMessage(const std::wstring &message)
|
|||
}
|
||||
}
|
||||
|
||||
void Server::sendRemoveNode(v3s16 p, u16 ignore_id)
|
||||
{
|
||||
JMutexAutoLock conlock(m_con_mutex);
|
||||
|
||||
// Create packet
|
||||
u32 replysize = 8;
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
writeU16(&reply[0], TOCLIENT_REMOVENODE);
|
||||
writeS16(&reply[2], p.X);
|
||||
writeS16(&reply[4], p.Y);
|
||||
writeS16(&reply[6], p.Z);
|
||||
|
||||
for(core::map<u16, RemoteClient*>::Iterator
|
||||
i = m_clients.getIterator();
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
// Get client and check that it is valid
|
||||
RemoteClient *client = i.getNode()->getValue();
|
||||
assert(client->peer_id == i.getNode()->getKey());
|
||||
if(client->serialization_version == SER_FMT_VER_INVALID)
|
||||
continue;
|
||||
|
||||
// Don't send if it's the same one
|
||||
if(client->peer_id == ignore_id)
|
||||
continue;
|
||||
|
||||
// Send as reliable
|
||||
m_con.Send(client->peer_id, 0, reply, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id)
|
||||
{
|
||||
for(core::map<u16, RemoteClient*>::Iterator
|
||||
i = m_clients.getIterator();
|
||||
i.atEnd() == false; i++)
|
||||
{
|
||||
// Get client and check that it is valid
|
||||
RemoteClient *client = i.getNode()->getValue();
|
||||
assert(client->peer_id == i.getNode()->getKey());
|
||||
if(client->serialization_version == SER_FMT_VER_INVALID)
|
||||
continue;
|
||||
|
||||
// Don't send if it's the same one
|
||||
if(client->peer_id == ignore_id)
|
||||
continue;
|
||||
|
||||
// Create packet
|
||||
u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
writeU16(&reply[0], TOCLIENT_ADDNODE);
|
||||
writeS16(&reply[2], p.X);
|
||||
writeS16(&reply[4], p.Y);
|
||||
writeS16(&reply[6], p.Z);
|
||||
n.serialize(&reply[8], client->serialization_version);
|
||||
|
||||
// Send as reliable
|
||||
m_con.Send(client->peer_id, 0, reply, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
/*
|
||||
Create a packet with the block in the right format
|
||||
*/
|
||||
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
block->serialize(os, ver);
|
||||
std::string s = os.str();
|
||||
SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
|
||||
|
||||
u32 replysize = 8 + blockdata.getSize();
|
||||
SharedBuffer<u8> reply(replysize);
|
||||
v3s16 p = block->getPos();
|
||||
writeU16(&reply[0], TOCLIENT_BLOCKDATA);
|
||||
writeS16(&reply[2], p.X);
|
||||
writeS16(&reply[4], p.Y);
|
||||
writeS16(&reply[6], p.Z);
|
||||
memcpy(&reply[8], *blockdata, blockdata.getSize());
|
||||
|
||||
/*dstream<<"Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
|
||||
<<": \tpacket size: "<<replysize<<std::endl;*/
|
||||
|
||||
/*
|
||||
Send packet
|
||||
*/
|
||||
m_con.Send(peer_id, 1, reply, true);
|
||||
}
|
||||
|
||||
void Server::SendBlocks(float dtime)
|
||||
{
|
||||
DSTACK(__FUNCTION_NAME);
|
||||
|
@ -3316,16 +3376,16 @@ Player *Server::emergePlayer(const char *name, const char *password,
|
|||
<<player->getName()<<"\""<<std::endl;
|
||||
|
||||
v2s16 nodepos;
|
||||
#if 1
|
||||
#if 0
|
||||
player->setPosition(intToFloat(v3s16(
|
||||
0,
|
||||
45, //64,
|
||||
0
|
||||
), BS));
|
||||
#endif
|
||||
#if 0
|
||||
f32 groundheight = 0;
|
||||
#if 0
|
||||
#if 1
|
||||
s16 groundheight = 0;
|
||||
#if 1
|
||||
// Try to find a good place a few times
|
||||
for(s32 i=0; i<500; i++)
|
||||
{
|
||||
|
@ -3334,12 +3394,20 @@ Player *Server::emergePlayer(const char *name, const char *password,
|
|||
nodepos = v2s16(-range + (myrand()%(range*2)),
|
||||
-range + (myrand()%(range*2)));
|
||||
v2s16 sectorpos = getNodeSectorPos(nodepos);
|
||||
/*
|
||||
Ignore position if it is near a chunk edge.
|
||||
Otherwise it would cause excessive loading time at
|
||||
initial generation
|
||||
*/
|
||||
{
|
||||
if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1))
|
||||
!= m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1)))
|
||||
continue;
|
||||
}
|
||||
// Get sector
|
||||
m_env.getMap().emergeSector(sectorpos);
|
||||
// Get ground height at point
|
||||
groundheight = m_env.getMap().getGroundHeight(nodepos, true);
|
||||
// The sector should have been generated -> groundheight exists
|
||||
assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
|
||||
groundheight = m_env.getServerMap().findGroundLevel(nodepos);
|
||||
// Don't go underwater
|
||||
if(groundheight < WATER_LEVEL)
|
||||
{
|
||||
|
@ -3384,10 +3452,9 @@ Player *Server::emergePlayer(const char *name, const char *password,
|
|||
|
||||
player->setPosition(intToFloat(v3s16(
|
||||
nodepos.X,
|
||||
//groundheight + 1,
|
||||
groundheight + 15,
|
||||
groundheight + 1,
|
||||
nodepos.Y
|
||||
)));
|
||||
), BS));
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
48
src/server.h
48
src/server.h
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <string>
|
||||
#include "utility.h"
|
||||
#include "porting.h"
|
||||
#include "map.h"
|
||||
|
||||
struct QueuedBlockEmerge
|
||||
{
|
||||
|
@ -341,12 +342,13 @@ private:
|
|||
u32 m_excess_gotblocks;
|
||||
};
|
||||
|
||||
class Server : public con::PeerHandler
|
||||
class Server : public con::PeerHandler, public MapEventReceiver
|
||||
{
|
||||
public:
|
||||
/*
|
||||
NOTE: Every public method should be thread-safe
|
||||
*/
|
||||
|
||||
Server(
|
||||
std::string mapsavedir
|
||||
);
|
||||
|
@ -361,23 +363,11 @@ public:
|
|||
void Receive();
|
||||
void ProcessData(u8 *data, u32 datasize, u16 peer_id);
|
||||
|
||||
// Environment and Connection must be locked when called
|
||||
void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver);
|
||||
|
||||
//
|
||||
|
||||
core::list<PlayerInfo> getPlayerInfo();
|
||||
|
||||
u32 getDayNightRatio()
|
||||
{
|
||||
s32 d = 8;
|
||||
s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
|
||||
if(t == d/4 || t == (d-d/4))
|
||||
return 600;
|
||||
else if(t < d/4 || t > (d-d/4))
|
||||
return 300;
|
||||
else
|
||||
return 1000;
|
||||
return time_to_daynight_ratio(m_time_of_day.get());
|
||||
}
|
||||
|
||||
bool getShutdownRequested()
|
||||
|
@ -385,6 +375,13 @@ public:
|
|||
return m_shutdown_requested.get();
|
||||
}
|
||||
|
||||
/*
|
||||
Shall be called with the environment locked.
|
||||
This is accessed by the map, which is inside the environment,
|
||||
so it shouldn't be a problem.
|
||||
*/
|
||||
void onMapEditEvent(MapEditEvent *event);
|
||||
|
||||
private:
|
||||
|
||||
// Virtual methods from con::PeerHandler.
|
||||
|
@ -398,6 +395,11 @@ private:
|
|||
void SendInventory(u16 peer_id);
|
||||
void SendChatMessage(u16 peer_id, const std::wstring &message);
|
||||
void BroadcastChatMessage(const std::wstring &message);
|
||||
void sendRemoveNode(v3s16 p, u16 ignore_id=0);
|
||||
void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0);
|
||||
|
||||
// Environment and Connection must be locked when called
|
||||
void SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver);
|
||||
|
||||
// Sends blocks to clients
|
||||
void SendBlocks(float dtime);
|
||||
|
@ -485,6 +487,24 @@ private:
|
|||
|
||||
MutexedVariable<bool> m_shutdown_requested;
|
||||
|
||||
/*
|
||||
Queue of map edits from the environment for sending to the clients
|
||||
This is behind m_env_mutex
|
||||
*/
|
||||
Queue<MapEditEvent*> m_unsent_map_edit_queue;
|
||||
/*
|
||||
Set to true when the server itself is modifying the map and does
|
||||
all sending of information by itself.
|
||||
This is behind m_env_mutex
|
||||
*/
|
||||
bool m_ignore_map_edit_events;
|
||||
/*
|
||||
If set to !=0, the incoming MapEditEvents are modified to have
|
||||
this peed id as the disabled recipient
|
||||
This is behind m_env_mutex
|
||||
*/
|
||||
u16 m_ignore_map_edit_events_peer_id;
|
||||
|
||||
friend class EmergeThread;
|
||||
friend class RemoteClient;
|
||||
};
|
||||
|
|
|
@ -88,35 +88,33 @@ extern "C"{
|
|||
}
|
||||
|
||||
/*
|
||||
Functions for calling from script:
|
||||
|
||||
object_set_base_position(self, x,y,z)
|
||||
x,y,z = object_get_base_position(self)
|
||||
object_add_message(self, data)
|
||||
n = object_get_node(self, x,y,z)
|
||||
object_remove(self)
|
||||
|
||||
Callbacks in script:
|
||||
|
||||
step(self, dtime)
|
||||
get_client_init_data(self)
|
||||
get_server_init_data(self)
|
||||
initialize(self, data)
|
||||
on_step(self, dtime)
|
||||
on_get_client_init_data(self)
|
||||
on_get_server_init_data(self)
|
||||
on_initialize(self, data)
|
||||
*/
|
||||
|
||||
/*
|
||||
object_set_base_position(self, x, y, z)
|
||||
object_set_base_position(self, {X=,Y=,Z=})
|
||||
*/
|
||||
static int lf_object_set_base_position(lua_State *L)
|
||||
{
|
||||
// 4: z
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 3: y
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 2: x
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
|
||||
|
@ -130,7 +128,7 @@ static int lf_object_set_base_position(lua_State *L)
|
|||
}
|
||||
|
||||
/*
|
||||
object_get_base_position(self)
|
||||
{X=,Y=,Z=} object_get_base_position(self)
|
||||
*/
|
||||
static int lf_object_get_base_position(lua_State *L)
|
||||
{
|
||||
|
@ -142,10 +140,21 @@ static int lf_object_get_base_position(lua_State *L)
|
|||
|
||||
v3f pos = self->getBasePosition();
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "X");
|
||||
lua_pushnumber(L, pos.X/BS);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "Y");
|
||||
lua_pushnumber(L, pos.Y/BS);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "Z");
|
||||
lua_pushnumber(L, pos.Z/BS);
|
||||
return 3; // Number of return values
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1; // Number of return values
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -178,18 +187,24 @@ static int lf_object_add_message(lua_State *L)
|
|||
}
|
||||
|
||||
/*
|
||||
object_get_node(self, x,y,z)
|
||||
object_get_node(self, {X=,Y=,Z=})
|
||||
*/
|
||||
static int lf_object_get_node(lua_State *L)
|
||||
{
|
||||
// 4: z
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 3: y
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 2: x
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
|
||||
|
@ -217,9 +232,6 @@ static int lf_object_get_node(lua_State *L)
|
|||
lua_pushstring(L, "param2");
|
||||
lua_pushinteger(L, n.param2);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "walkable");
|
||||
lua_pushboolean(L, content_features(n.d).walkable);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Return the table
|
||||
return 1;
|
||||
|
@ -227,39 +239,82 @@ static int lf_object_get_node(lua_State *L)
|
|||
|
||||
#if 0
|
||||
/*
|
||||
object_set_node(self, x,y,z, n)
|
||||
get_node_features(node)
|
||||
node = {content=,param1=,param2=}
|
||||
*/
|
||||
static int lf_object_set_node(lua_State *L)
|
||||
static int lf_get_node_features(lua_State *L)
|
||||
{
|
||||
MapNode n;
|
||||
|
||||
// 5: n
|
||||
// Get fields of table
|
||||
|
||||
lua_pushinteger(L, "content");
|
||||
// 1: node
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "content");
|
||||
lua_gettable(L, -2);
|
||||
n.d = lua_tonumber(L, -1);
|
||||
n.d = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushinteger(L, "param1");
|
||||
lua_pushstring(L, "param1");
|
||||
lua_gettable(L, -2);
|
||||
n.param = lua_tonumber(L, -1);
|
||||
n.param = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushinteger(L, "param2");
|
||||
lua_pushstring(L, "param2");
|
||||
lua_gettable(L, -2);
|
||||
n.param2 = lua_tonumber(L, -1);
|
||||
n.param2 = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
ContentFeatures &f = content_features(n.d);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
get_content_features(content)
|
||||
*/
|
||||
static int lf_get_content_features(lua_State *L)
|
||||
{
|
||||
MapNode n;
|
||||
|
||||
// 1: content
|
||||
n.d = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 4: z
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
|
||||
// Get and return information
|
||||
ContentFeatures &f = content_features(n.d);
|
||||
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "walkable");
|
||||
lua_pushboolean(L, f.walkable);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "diggable");
|
||||
lua_pushboolean(L, f.diggable);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "buildable_to");
|
||||
lua_pushboolean(L, f.buildable_to);
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
bool object_dig_node(self, {X=,Y=,Z=})
|
||||
Return true on success
|
||||
*/
|
||||
static int lf_object_dig_node(lua_State *L)
|
||||
{
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 3: y
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// 2: x
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
|
||||
|
@ -269,26 +324,79 @@ static int lf_object_set_node(lua_State *L)
|
|||
|
||||
v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
|
||||
|
||||
/*dstream<<"Checking node from pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z
|
||||
<<")"<<std::endl;*/
|
||||
/*
|
||||
Do stuff.
|
||||
This gets sent to the server by the map through the edit
|
||||
event system.
|
||||
*/
|
||||
bool succeeded = self->getEnv()->getMap().removeNodeWithEvent(pos);
|
||||
|
||||
// Get the node
|
||||
MapNode n(CONTENT_IGNORE);
|
||||
n = self->getEnv()->getMap().getNodeNoEx(pos);
|
||||
|
||||
// Create a table with some data about the node
|
||||
lua_newtable(L);
|
||||
lua_pushstring(L, "content");
|
||||
lua_pushinteger(L, n.d);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "walkable");
|
||||
lua_pushboolean(L, content_features(n.d).walkable);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Return the table
|
||||
lua_pushboolean(L, succeeded);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
bool object_place_node(self, {X=,Y=,Z=}, node)
|
||||
node={content=,param1=,param2=}
|
||||
param1 and param2 are optional
|
||||
Return true on success
|
||||
*/
|
||||
static int lf_object_place_node(lua_State *L)
|
||||
{
|
||||
// 3: node
|
||||
MapNode n(CONTENT_STONE);
|
||||
assert(lua_istable(L, -1));
|
||||
{
|
||||
lua_pushstring(L, "content");
|
||||
lua_gettable(L, -2);
|
||||
if(lua_isnumber(L, -1))
|
||||
n.d = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, "param1");
|
||||
lua_gettable(L, -2);
|
||||
if(lua_isnumber(L, -1))
|
||||
n.param = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, "param2");
|
||||
lua_gettable(L, -2);
|
||||
if(lua_isnumber(L, -1))
|
||||
n.param2 = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
// 2: position
|
||||
assert(lua_istable(L, -1));
|
||||
lua_pushstring(L, "X");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number x = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, "Y");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number y = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, "Z");
|
||||
lua_gettable(L, -2);
|
||||
lua_Number z = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
// 1: self
|
||||
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
assert(self);
|
||||
|
||||
v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
|
||||
|
||||
/*
|
||||
Do stuff.
|
||||
This gets sent to the server by the map through the edit
|
||||
event system.
|
||||
*/
|
||||
bool succeeded = self->getEnv()->getMap().addNodeWithEvent(pos, n);
|
||||
|
||||
lua_pushboolean(L, succeeded);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
object_remove(x,y,z)
|
||||
|
@ -306,6 +414,38 @@ static int lf_object_remove(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
{X=,Y=,Z=} object_get_nearest_player_position(self)
|
||||
*/
|
||||
/*static int lf_object_get_nearest_player_position(lua_State *L)
|
||||
{
|
||||
// 1: self
|
||||
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
assert(self);
|
||||
|
||||
ServerEnvironment *env = self->getEnv();
|
||||
env->
|
||||
v3f pos = ;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "X");
|
||||
lua_pushnumber(L, pos.X/BS);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "Y");
|
||||
lua_pushnumber(L, pos.Y/BS);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "Z");
|
||||
lua_pushnumber(L, pos.Z/BS);
|
||||
lua_settable(L, -3);
|
||||
|
||||
return 1; // Number of return values
|
||||
}*/
|
||||
|
||||
LuaSAO::LuaSAO(ServerEnvironment *env, u16 id, v3f pos):
|
||||
ServerActiveObject(env, id, pos),
|
||||
L(NULL)
|
||||
|
@ -329,6 +469,9 @@ LuaSAO::LuaSAO(ServerEnvironment *env, u16 id, v3f pos):
|
|||
lua_register(L, "object_get_base_position", lf_object_get_base_position);
|
||||
lua_register(L, "object_add_message", lf_object_add_message);
|
||||
lua_register(L, "object_get_node", lf_object_get_node);
|
||||
lua_register(L, "get_content_features", lf_get_content_features);
|
||||
lua_register(L, "object_dig_node", lf_object_dig_node);
|
||||
lua_register(L, "object_place_node", lf_object_place_node);
|
||||
lua_register(L, "object_remove", lf_object_remove);
|
||||
}
|
||||
|
||||
|
@ -372,7 +515,7 @@ std::string LuaSAO::getClientInitializationData()
|
|||
|
||||
do{
|
||||
|
||||
const char *funcname = "get_client_init_data";
|
||||
const char *funcname = "on_get_client_init_data";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
@ -430,7 +573,7 @@ std::string LuaSAO::getServerInitializationData()
|
|||
|
||||
do{
|
||||
|
||||
const char *funcname = "get_server_init_data";
|
||||
const char *funcname = "on_get_server_init_data";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
@ -474,7 +617,41 @@ std::string LuaSAO::getServerInitializationData()
|
|||
return data;
|
||||
}
|
||||
|
||||
void LuaSAO::initialize(const std::string &data)
|
||||
void LuaSAO::initializeFromNothing(const std::string &script_name)
|
||||
{
|
||||
loadScripts(script_name);
|
||||
|
||||
/*
|
||||
Call on_initialize(self, data) in the script
|
||||
*/
|
||||
|
||||
const char *funcname = "on_initialize";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
lua_pop(L,1);
|
||||
dstream<<"WARNING: LuaSAO: Function not found: "
|
||||
<<funcname<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Parameters:
|
||||
// 1: self
|
||||
lua_pushlightuserdata(L, this);
|
||||
// 2: data (other)
|
||||
lua_pushstring(L, "");
|
||||
|
||||
// Call (2 parameters, 0 result)
|
||||
if(lua_pcall(L, 2, 0, 0))
|
||||
{
|
||||
dstream<<"WARNING: LuaSAO: Error running function "
|
||||
<<funcname<<": "
|
||||
<<lua_tostring(L,-1)<<std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LuaSAO::initializeFromSave(const std::string &data)
|
||||
{
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
std::string script_name = deSerializeString(is);
|
||||
|
@ -483,10 +660,10 @@ void LuaSAO::initialize(const std::string &data)
|
|||
loadScripts(script_name);
|
||||
|
||||
/*
|
||||
Call initialize(self, data) in the script
|
||||
Call on_initialize(self, data) in the script
|
||||
*/
|
||||
|
||||
const char *funcname = "initialize";
|
||||
const char *funcname = "on_initialize";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
|
@ -548,11 +725,13 @@ void LuaSAO::loadScripts(const std::string &script_name)
|
|||
|
||||
void LuaSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
|
||||
{
|
||||
lua_getglobal(L, "step");
|
||||
const char *funcname = "on_step";
|
||||
lua_getglobal(L, funcname);
|
||||
if(!lua_isfunction(L,-1))
|
||||
{
|
||||
lua_pop(L,1);
|
||||
dstream<<"WARNING: LuaSAO::step(): step function not found"<<std::endl;
|
||||
dstream<<"WARNING: LuaSAO::step(): Function not found: "
|
||||
<<funcname<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -565,7 +744,8 @@ void LuaSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
|
|||
// Call (2 parameters, 0 result)
|
||||
if(lua_pcall(L, 2, 0, 0))
|
||||
{
|
||||
dstream<<"WARNING: LuaSAO::step(): Error running function step(): "
|
||||
dstream<<"WARNING: LuaSAO::step(): Error running function "
|
||||
<<funcname<<": "
|
||||
<<lua_tostring(L,-1)<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -131,10 +131,10 @@ public:
|
|||
}
|
||||
|
||||
virtual std::string getClientInitializationData();
|
||||
|
||||
virtual std::string getServerInitializationData();
|
||||
|
||||
void initialize(const std::string &data);
|
||||
void initializeFromNothing(const std::string &script_name);
|
||||
void initializeFromSave(const std::string &data);
|
||||
|
||||
void loadScripts(const std::string &script_name);
|
||||
|
||||
|
|
|
@ -1832,7 +1832,11 @@ inline std::string deSerializeString(std::istream &is)
|
|||
{
|
||||
char buf[2];
|
||||
is.read(buf, 2);
|
||||
if(is.gcount() != 2)
|
||||
throw SerializationError("deSerializeString: size not read");
|
||||
u16 s_size = readU16((u8*)buf);
|
||||
if(s_size == 0)
|
||||
return "";
|
||||
Buffer<char> buf2(s_size);
|
||||
is.read(&buf2[0], s_size);
|
||||
std::string s;
|
||||
|
@ -1867,7 +1871,11 @@ inline std::string deSerializeLongString(std::istream &is)
|
|||
{
|
||||
char buf[4];
|
||||
is.read(buf, 4);
|
||||
if(is.gcount() != 4)
|
||||
throw SerializationError("deSerializeLongString: size not read");
|
||||
u32 s_size = readU32((u8*)buf);
|
||||
if(s_size == 0)
|
||||
return "";
|
||||
Buffer<char> buf2(s_size);
|
||||
is.read(&buf2[0], s_size);
|
||||
std::string s;
|
||||
|
|
Loading…
Reference in New Issue