mainly work on object scripting api

This commit is contained in:
Perttu Ahola 2011-02-23 02:49:57 +02:00
parent eef7bc3570
commit 9778347c7f
19 changed files with 962 additions and 430 deletions

@ -1,7 +1,7 @@
-- Client-side code of the test lua object -- Client-side code of the test lua object
-- --
-- Some helper functions -- Some helper functions and classes
-- --
function split(str, pat) function split(str, pat)
@ -23,6 +23,7 @@ function split(str, pat)
return t return t
end end
-- For debugging
function dump(o) function dump(o)
if type(o) == 'table' then if type(o) == 'table' then
local s = '{ ' local s = '{ '
@ -36,29 +37,90 @@ function dump(o)
end end
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 -- Actual code
-- --
function step(self, dtime) pos_trans = SmoothTranslator.create()
-- Some smoother animation could be done here 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 end
function process_message(self, data) function on_process_message(self, data)
--print("client got message: " .. data) --print("client got message: " .. data)
-- Receive our custom messages -- Receive our custom messages
sp = split(data, " ") sp = split(data, " ")
if sp[1] == "pos" then 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 end
if sp[1] == "rot" then 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
end end
function initialize(self, data) function on_initialize(self, data)
print("client object got initialization: " .. data) print("client object got initialization: " .. data)
corners = { corners = {

@ -1,24 +1,42 @@
-- Server-side code of the test lua object -- 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 counter = 0
counter2 = 0 counter2 = 0
counter3 = 0
counter4 = 0
death_counter = 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} rotation = {X=0, Y=math.random(0,360), Z=0}
dir = 1 dir = 1
temp1 = 0
function step(self, dtime) function on_step(self, dtime)
--[[if position.Y > 9.5 then --[[if position.Y > 9.5 then
position.Y = 6 position.Y = 6
end end
if position.Y < 5.5 then if position.Y < 5.5 then
position.Y = 9 position.Y = 9]]
counter2 = counter2 - dtime
if counter2 < 0 then
counter2 = counter2 + 3
dir = -dir
end]]
-- Limit step to a sane value; it jumps a lot while the map generator -- Limit step to a sane value; it jumps a lot while the map generator
-- is in action -- is in action
@ -27,16 +45,19 @@ function step(self, dtime)
end end
-- Returned value has these fields: -- Returned value has these fields:
-- * bool walkable
-- * int content -- * int content
n = object_get_node(self, position.X,position.Y-0.35,position.Z) -- * int param1
if n.walkable then -- * 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 dir = 1
else else
dir = -1 dir = -1
end end
-- Keep the object approximately at ground level -- 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 -- Move the object around
position.X = position.X + math.cos(math.pi+rotation.Y/180*math.pi) 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 -- This value has to be set; it determines to which player the
-- object is near to and such -- 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 -- Send some custom messages at a custom interval
counter = counter - dtime counter = counter - dtime
if counter < 0 then 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 message = "pos " .. position.X .. " " .. position.Y .. " " .. position.Z
object_add_message(self, message) object_add_message(self, message)
@ -63,6 +101,20 @@ function step(self, dtime)
object_add_message(self, message) object_add_message(self, message)
end 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 -- Remove the object after some time
death_counter = death_counter + dtime death_counter = death_counter + dtime
if death_counter > 30 then if death_counter > 30 then
@ -72,19 +124,31 @@ function step(self, dtime)
end end
-- This stuff is passed to a newly created client-side counterpart of this object -- 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" return "result of get_client_init_data"
end end
-- This should return some data that mostly saves the state of this object -- This should return some data that mostly saves the state of this object
-- Not implemented yet -- Not completely implemented yet
function get_server_init_data(self) function on_get_server_init_data(self)
-- Just return some data for testing
return "result of get_server_init_data" return "result of get_server_init_data"
end end
-- At reload time, the output of get_server_init_data is passed to this -- When the object is loaded from scratch, this is called before the first
-- Not implemented yet -- on_step(). Data is an empty string or the output of an object spawner
function initialize(self, data) -- 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) print("server object got initialization: " .. data)
position = object_get_base_position(self)
end end

@ -18,7 +18,7 @@
#screenW = 800 #screenW = 800
#screenH = 600 #screenH = 600
#port = 30000 #port = 30000
#address = kray.dy.fi #address =
#name = #name =
# Whether to try to fog out the border of the visible area # Whether to try to fog out the border of the visible area
@ -44,14 +44,14 @@
# Server side stuff # Server side stuff
# #
#map-dir = /home/palle/custom_map # Set to true to enable experimental features
#enable_experimental = false
#plants_amount = 1.0 #map-dir = /home/palle/custom_map
#ravines_amount = 1.0
#coal_amount = 1.0
# Set to true to enable creative mode (unlimited inventory) # Set to true to enable creative mode (unlimited inventory)
#creative_mode = false #creative_mode = false
# Player and object positions are sent at intervals specified by this # Player and object positions are sent at intervals specified by this
#objectdata_inverval = 0.2 #objectdata_inverval = 0.2

@ -989,27 +989,14 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
12000 = midday 12000 = midday
*/ */
{ {
const s32 daylength = 16; u32 dr = time_to_daynight_ratio(m_time_of_day.get());
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;
dstream<<"time_of_day="<<m_time_of_day.get() dstream<<"Client: time_of_day="<<m_time_of_day.get()
<<", t="<<t
<<", dr="<<dr <<", dr="<<dr
<<std::endl; <<std::endl;
if(dr != m_env.getDayNightRatio()) if(dr != m_env.getDayNightRatio())
{ {
//dstream<<"dr="<<dr<<std::endl;
dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl; dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
m_env.setDayNightRatio(dr); m_env.setDayNightRatio(dr);
m_env.expireMeshes(true); m_env.expireMeshes(true);
@ -1595,7 +1582,7 @@ void Client::addNode(v3s16 p, MapNode n)
void Client::updateCamera(v3f pos, v3f dir) void Client::updateCamera(v3f pos, v3f dir)
{ {
m_env.getMap().updateCamera(pos, dir); m_env.getClientMap().updateCamera(pos, dir);
camera_position = pos; camera_position = pos;
camera_direction = dir; 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: Callbacks in script:
step(self, dtime) on_step(self, dtime)
process_message(self, data) on_process_message(self, data)
initialize(self, data) on_initialize(self, data)
TODO: 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) static int lf_object_set_position(lua_State *L)
{ {
// 4: z // 2: position
lua_Number z = lua_tonumber(L, -1); 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_pop(L, 1);
// 3: y lua_pushstring(L, "Y");
lua_gettable(L, -2);
lua_Number y = lua_tonumber(L, -1); lua_Number y = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
// 2: x lua_pushstring(L, "Z");
lua_Number x = lua_tonumber(L, -1); lua_gettable(L, -2);
lua_Number z = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pop(L, 1); lua_pop(L, 1);
// 1: self // 1: self
LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1); 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) static int lf_object_set_rotation(lua_State *L)
{ {
// 4: z // 2: position
lua_Number z = lua_tonumber(L, -1); 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_pop(L, 1);
// 3: y lua_pushstring(L, "Y");
lua_gettable(L, -2);
lua_Number y = lua_tonumber(L, -1); lua_Number y = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
// 2: x lua_pushstring(L, "Z");
lua_Number x = lua_tonumber(L, -1); lua_gettable(L, -2);
lua_Number z = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pop(L, 1); lua_pop(L, 1);
// 1: self // 1: self
LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1); LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
@ -381,7 +388,7 @@ void LuaCAO::step(float dtime)
Call step(self, dtime) from lua Call step(self, dtime) from lua
*/ */
const char *funcname = "step"; const char *funcname = "on_step";
lua_getglobal(L, funcname); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) if(!lua_isfunction(L,-1))
{ {
@ -413,7 +420,7 @@ void LuaCAO::processMessage(const std::string &data)
Call process_message(self, data) from lua Call process_message(self, data) from lua
*/ */
const char *funcname = "process_message"; const char *funcname = "on_process_message";
lua_getglobal(L, funcname); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) if(!lua_isfunction(L,-1))
{ {
@ -462,7 +469,7 @@ void LuaCAO::initialize(const std::string &data)
Call initialize(self, data) in the script Call initialize(self, data) in the script
*/ */
const char *funcname = "initialize"; const char *funcname = "on_initialize";
lua_getglobal(L, funcname); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) if(!lua_isfunction(L,-1))
{ {

@ -51,7 +51,9 @@ void set_default_settings()
g_settings.setDefault("fast_move", "false"); g_settings.setDefault("fast_move", "false");
// Server stuff // 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("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");

@ -96,6 +96,25 @@ Player * Environment::getPlayer(const char *name)
return NULL; 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() core::list<Player*> Environment::getPlayers()
{ {
return m_players; return m_players;
@ -147,8 +166,9 @@ u32 Environment::getDayNightRatio()
ServerEnvironment ServerEnvironment
*/ */
ServerEnvironment::ServerEnvironment(ServerMap *map): ServerEnvironment::ServerEnvironment(ServerMap *map, Server *server):
m_map(map), m_map(map),
m_server(server),
m_random_spawn_timer(0) m_random_spawn_timer(0)
{ {
} }
@ -382,6 +402,9 @@ void ServerEnvironment::step(float dtime)
} }
} }
if(g_settings.getBool("enable_experimental"))
{
/* /*
Step active objects 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)));*/ 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), /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
myrand_range(2*BS,9*BS), s16 y = 1 + getServerMap().findGroundLevel(p2d);
myrand_range(-2*BS,2*BS)) 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 Select a random type for it
*/ */
std::string objectdir = porting::getDataPath("luaobjects"); std::string objectdir = porting::getDataPath("luaobjects");
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(objectdir); std::vector<fs::DirListNode> dirlist = fs::GetDirListing(objectdir);
if(dirlist.size() > 0)
{
u32 selected_i = myrand_range(0, dirlist.size()-1); u32 selected_i = myrand_range(0, dirlist.size()-1);
std::string selected_name = ""; std::string selected_name = "";
selected_name = dirlist[selected_i].name; selected_name = dirlist[selected_i].name;
@ -469,11 +509,14 @@ void ServerEnvironment::step(float dtime)
/* /*
Load the scripts for the type Load the scripts for the type
*/ */
obj->loadScripts(selected_name.c_str()); obj->initializeFromNothing(selected_name.c_str());
// Add the object to the environment // Add the object to the environment
addActiveObject(obj); addActiveObject(obj);
} }
}
} // enable_experimental
} }
ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)

@ -57,6 +57,7 @@ public:
void removePlayer(u16 peer_id); void removePlayer(u16 peer_id);
Player * getPlayer(u16 peer_id); Player * getPlayer(u16 peer_id);
Player * getPlayer(const char *name); Player * getPlayer(const char *name);
Player * getRandomConnectedPlayer();
core::list<Player*> getPlayers(); core::list<Player*> getPlayers();
core::list<Player*> getPlayers(bool ignore_disconnected); core::list<Player*> getPlayers(bool ignore_disconnected);
void printPlayers(std::ostream &o); void printPlayers(std::ostream &o);
@ -79,10 +80,12 @@ protected:
#include "serverobject.h" #include "serverobject.h"
class Server;
class ServerEnvironment : public Environment class ServerEnvironment : public Environment
{ {
public: public:
ServerEnvironment(ServerMap *map); ServerEnvironment(ServerMap *map, Server *server);
~ServerEnvironment(); ~ServerEnvironment();
Map & getMap() Map & getMap()
@ -95,6 +98,11 @@ public:
return *m_map; return *m_map;
} }
Server * getServer()
{
return m_server;
}
void step(f32 dtime); void step(f32 dtime);
void serializePlayers(const std::string &savedir); void serializePlayers(const std::string &savedir);
@ -140,6 +148,7 @@ public:
private: private:
ServerMap *m_map; ServerMap *m_map;
Server *m_server;
core::map<u16, ServerActiveObject*> m_active_objects; core::map<u16, ServerActiveObject*> m_active_objects;
Queue<ActiveObjectMessage> m_active_object_messages; Queue<ActiveObjectMessage> m_active_object_messages;
float m_random_spawn_timer; float m_random_spawn_timer;

@ -199,15 +199,6 @@ FIXME: Server went into some infinite PeerNotFoundException loop
Objects: 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 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. 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 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 * not done
=== Fixmes === Fixmes
* Check the fixmes in the list above
* Make server find the spawning place from the real map data, not from * Make server find the spawning place from the real map data, not from
the heightmap the heightmap
- But the changing borders of chunk have to be avoided, because - 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 * Make the generator to run in background and not blocking block
placement and transfer placement and transfer
* only_from_disk might not work anymore - check and fix it. * only_from_disk might not work anymore - check and fix it.
* Check the fixmes in the list above
=== Making it more portable === Making it more portable
* Some MSVC: std::sto* are defined without a namespace and collide * Some MSVC: std::sto* are defined without a namespace and collide
with the ones in utility.h with the ones in utility.h
=== Features === 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 * Add mud underground
* Make an "environment metafile" to store at least time of day * Make an "environment metafile" to store at least time of day
* Move digging property stuff from material.{h,cpp} to mapnode.cpp... * 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 * Add some kind of erosion and other stuff that now is possible
* Make client to fetch stuff asynchronously * Make client to fetch stuff asynchronously
- Needs method SyncProcessData - 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 * Better water generation (spread it to underwater caverns but don't
fill dungeons that don't touch big water masses) fill dungeons that don't touch big water masses)
* When generating a chunk and the neighboring chunk doesn't have mud * 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 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 the other chunk making nasty straight walls when the other chunk
is generated. Fix it. 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 * Make a small history check to transformLiquids to detect and log
continuous oscillations, in such detail that they can be fixed. continuous oscillations, in such detail that they can be fixed.
* Combine meshes to bigger ones in ClientMap and set them EHM_STATIC * 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; dstream<<"Created main menu"<<std::endl;
while(g_device->run()) while(g_device->run() && kill == false)
{ {
// Run global IrrlichtWrapper's main thread processing stuff // Run global IrrlichtWrapper's main thread processing stuff
g_irrlicht->Run(); g_irrlicht->Run();
@ -1811,7 +1804,7 @@ int main(int argc, char *argv[])
} }
// Break out of menu-game loop to shut down cleanly // Break out of menu-game loop to shut down cleanly
if(g_device->run() == false) if(g_device->run() == false || kill == true)
break; break;
dstream<<"Dropping main menu"<<std::endl; dstream<<"Dropping main menu"<<std::endl;

@ -34,30 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Map::Map(std::ostream &dout): Map::Map(std::ostream &dout):
m_dout(dout), m_dout(dout),
m_camera_position(0,0,0),
m_camera_direction(0,0,1),
m_sector_cache(NULL) m_sector_cache(NULL)
{ {
m_sector_mutex.Init(); m_sector_mutex.Init();
m_camera_mutex.Init();
assert(m_sector_mutex.IsInitialized()); 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() Map::~Map()
{ {
/* /*
Stop updater thread Free all MapSectors
*/
/*updater.setRun(false);
while(updater.IsRunning())
sleep_s(1);*/
/*
Free all MapSectors.
*/ */
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator(); core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
for(; i.atEnd() == false; i++) 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) MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
{ {
if(m_sector_cache != NULL && p == m_sector_cache_p){ if(m_sector_cache != NULL && p == m_sector_cache_p){
@ -145,34 +154,6 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
return block; 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) bool Map::isNodeUnderground(v3s16 p)
{ {
v3s16 blockpos = getNodeBlockPos(p); v3s16 blockpos = getNodeBlockPos(p);
@ -947,8 +928,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
assert(block != NULL); assert(block != NULL);
modified_blocks.insert(blockpos, block); modified_blocks.insert(blockpos, block);
if(isValidPosition(p) == false) assert(isValidPosition(p));
throw;
// Unlight neighbours of node. // Unlight neighbours of node.
// This means setting light of all consequent dimmer nodes // This means setting light of all consequent dimmer nodes
@ -1161,7 +1141,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
} }
catch(InvalidPositionException &e) 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) bool Map::dayNightDiffed(v3s16 blockpos)
{ {
try{ try{
@ -4535,6 +4572,42 @@ MapBlock * ServerMap::emergeBlock(
return block; 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) void ServerMap::createDir(std::string path)
{ {
if(fs::CreateDir(path) == false) 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) void ServerMap::PrintInfo(std::ostream &out)
{ {
out<<"ServerMap: "; out<<"ServerMap: ";
@ -5165,20 +5216,15 @@ ClientMap::ClientMap(
Map(dout_client), Map(dout_client),
scene::ISceneNode(parent, mgr, id), scene::ISceneNode(parent, mgr, id),
m_client(client), 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, m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000); BS*1000000,BS*1000000,BS*1000000);
//setPosition(v3f(BS,BS,BS));
} }
ClientMap::~ClientMap() ClientMap::~ClientMap()

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_SERVER 1
#define MAPTYPE_CLIENT 2 #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 class Map : public NodeContainer
{ {
public: public:
@ -69,24 +114,10 @@ public:
delete this; delete this;
} }
void updateCamera(v3f pos, v3f dir) void addEventReceiver(MapEventReceiver *event_receiver);
{ void removeEventReceiver(MapEventReceiver *event_receiver);
JMutexAutoLock lock(m_camera_mutex); // event shall be deleted by caller after the call.
m_camera_position = pos; void dispatchEvent(MapEditEvent *event);
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
);
}
// On failure returns NULL // On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d); MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
@ -112,10 +143,6 @@ public:
// Gets an existing block or creates an empty one // Gets an existing block or creates an empty one
//MapBlock * getBlockCreate(v3s16 p); //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 // Returns InvalidPositionException if not found
bool isNodeUnderground(v3s16 p); bool isNodeUnderground(v3s16 p);
@ -212,6 +239,14 @@ public:
void removeNodeAndUpdate(v3s16 p, void removeNodeAndUpdate(v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks); 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 Takes the blocks at the edges into account
*/ */
@ -249,13 +284,12 @@ protected:
std::ostream &m_dout; 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; core::map<v2s16, MapSector*> m_sectors;
JMutex m_sector_mutex; 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 // Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache; MapSector *m_sector_cache;
v2s16 m_sector_cache_p; v2s16 m_sector_cache_p;
@ -445,6 +479,9 @@ public:
); );
#endif #endif
// Helper for placing objects on ground level
s16 findGroundLevel(v2s16 p2d);
/* /*
Misc. helper functions for fiddling with directory and file Misc. helper functions for fiddling with directory and file
names when saving names when saving
@ -489,10 +526,6 @@ public:
// This will generate a sector with getSector if not found. // This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector); void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
// Gets from master heightmap
// DEPRECATED?
void getSectorCorners(v2s16 p2d, s16 *corners);
// For debug printing // For debug printing
virtual void PrintInfo(std::ostream &out); virtual void PrintInfo(std::ostream &out);
@ -573,6 +606,13 @@ public:
ISceneNode::drop(); 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 Forcefully get a sector from somewhere
*/ */
@ -640,6 +680,11 @@ private:
//JMutex mesh_mutex; //JMutex mesh_mutex;
MapDrawControl &m_control; MapDrawControl &m_control;
v3f m_camera_position;
v3f m_camera_direction;
JMutex m_camera_mutex;
}; };
#endif #endif

@ -19,10 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblockobject.h" #include "mapblockobject.h"
#include "mapblock.h" #include "mapblock.h"
// Only for ::getNodeBox, TODO: Get rid of this // For object wrapping
#include "map.h" #include "map.h"
#include "inventory.h" #include "inventory.h"
#include "irrlichtwrapper.h" #include "irrlichtwrapper.h"
#include "utility.h"
/* /*
MapBlockObject MapBlockObject
@ -168,8 +169,7 @@ void MovingObject::move(float dtime, v3f acceleration)
// walking over map borders // walking over map borders
} }
core::aabbox3d<f32> nodebox = Map::getNodeBox( core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
v3s16(x,y,z));
// See if the object is touching ground // See if the object is touching ground
if( if(

@ -428,8 +428,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d)
// walking over map borders // walking over map borders
} }
core::aabbox3d<f32> nodebox = Map::getNodeBox( core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
v3s16(x,y,z));
/* /*
See if the player is touching ground. See if the player is touching ground.

@ -51,7 +51,7 @@ void sigint_handler(int sig)
dstream<<DTIME<<"INFO: sigint_handler(): " dstream<<DTIME<<"INFO: sigint_handler(): "
<<"Ctrl-C pressed, shutting down."<<std::endl; <<"Ctrl-C pressed, shutting down."<<std::endl;
dstream<<DTIME<<"INFO: siging_handler(): " dstream<<DTIME<<"INFO: sigint_handler(): "
<<"Printing debug stacks"<<std::endl; <<"Printing debug stacks"<<std::endl;
debug_stacks_print(); debug_stacks_print();

@ -893,7 +893,7 @@ u32 PIChecksum(core::list<PlayerInfo> &l)
Server::Server( Server::Server(
std::string mapsavedir std::string mapsavedir
): ):
m_env(new ServerMap(mapsavedir)), m_env(new ServerMap(mapsavedir), this),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_thread(this), m_thread(this),
m_emergethread(this), m_emergethread(this),
@ -902,9 +902,10 @@ Server::Server(
m_time_of_day_send_timer(0), m_time_of_day_send_timer(0),
m_uptime(0), m_uptime(0),
m_mapsavedir(mapsavedir), 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_liquid_transform_timer = 0.0;
m_print_info_timer = 0.0; m_print_info_timer = 0.0;
m_objectdata_timer = 0.0; m_objectdata_timer = 0.0;
@ -916,6 +917,8 @@ Server::Server(
m_step_dtime_mutex.Init(); m_step_dtime_mutex.Init();
m_step_dtime = 0.0; m_step_dtime = 0.0;
m_env.getMap().addEventReceiver(this);
// Load players // Load players
m_env.deSerializePlayers(m_mapsavedir); m_env.deSerializePlayers(m_mapsavedir);
} }
@ -1191,6 +1194,9 @@ void Server::AsyncRunStep()
} }
} }
if(g_settings.getBool("enable_experimental"))
{
/* /*
Check added and deleted active objects Check added and deleted active objects
*/ */
@ -1198,14 +1204,18 @@ void Server::AsyncRunStep()
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex); JMutexAutoLock conlock(m_con_mutex);
// Radius inside which objects are active
s16 radius = 32;
for(core::map<u16, RemoteClient*>::Iterator for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator(); i = m_clients.getIterator();
i.atEnd() == false; i++) i.atEnd() == false; i++)
{ {
RemoteClient *client = i.getNode()->getValue(); RemoteClient *client = i.getNode()->getValue();
Player *player = m_env.getPlayer(client->peer_id); Player *player = m_env.getPlayer(client->peer_id);
if(player==NULL)
continue;
v3s16 pos = floatToInt(player->getPosition(), BS); v3s16 pos = floatToInt(player->getPosition(), BS);
s16 radius = 32;
core::map<u16, bool> removed_objects; core::map<u16, bool> removed_objects;
core::map<u16, bool> added_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 Send object positions
TODO: Get rid of MapBlockObjects
*/ */
{ {
float &counter = m_objectdata_timer; 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 Send the removal to all other clients
*/ */
sendRemoveNode(p_over, peer_id);
// 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);
}
/* /*
Update and send inventory Update and send inventory
@ -2063,33 +2084,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
Remove the node Remove the node
(this takes some time so it is done after the quick stuff) (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); m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
m_ignore_map_edit_events = false;
#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
} }
/* /*
@ -2151,16 +2148,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(content_features(n.d).wall_mounted) if(content_features(n.d).wall_mounted)
n.dir = packDir(p_under - p_over); n.dir = packDir(p_under - p_over);
// Create packet /*
u32 replysize = 8 + MapNode::serializedLength(peer_ser_ver); Send to all players
SharedBuffer<u8> reply(replysize); */
writeU16(&reply[0], TOCLIENT_ADDNODE); sendAddNode(p_over, n, 0);
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);
/* /*
Handle inventory 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 This takes some time so it is done after the quick stuff
*/ */
core::map<v3s16, MapBlock*> modified_blocks; core::map<v3s16, MapBlock*> modified_blocks;
m_ignore_map_edit_events = true;
m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
m_ignore_map_edit_events = false;
/* /*
Calculate special events 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, void Server::onMapEditEvent(MapEditEvent *event)
SharedBuffer<u8> data, bool reliable)
{ {
JMutexAutoLock lock(m_con_mutex); dstream<<"Server::onMapEditEvent()"<<std::endl;
m_con.Send(peer_id, channelnum, data, reliable); if(m_ignore_map_edit_events)
}*/ return;
MapEditEvent *e = event->clone();
void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) m_unsent_map_edit_queue.push_back(e);
{
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);
} }
core::list<PlayerInfo> Server::getPlayerInfo() core::list<PlayerInfo> Server::getPlayerInfo()
@ -2759,6 +2724,10 @@ void Server::SendPlayerInfos()
m_con.SendToAll(0, data, true); m_con.SendToAll(0, data, true);
} }
/*
Craft checking system
*/
enum ItemSpecType enum ItemSpecType
{ {
ITEM_NONE, 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) void Server::SendBlocks(float dtime)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
@ -3316,16 +3376,16 @@ Player *Server::emergePlayer(const char *name, const char *password,
<<player->getName()<<"\""<<std::endl; <<player->getName()<<"\""<<std::endl;
v2s16 nodepos; v2s16 nodepos;
#if 1 #if 0
player->setPosition(intToFloat(v3s16( player->setPosition(intToFloat(v3s16(
0, 0,
45, //64, 45, //64,
0 0
), BS)); ), BS));
#endif #endif
#if 0 #if 1
f32 groundheight = 0; s16 groundheight = 0;
#if 0 #if 1
// Try to find a good place a few times // Try to find a good place a few times
for(s32 i=0; i<500; i++) 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)), nodepos = v2s16(-range + (myrand()%(range*2)),
-range + (myrand()%(range*2))); -range + (myrand()%(range*2)));
v2s16 sectorpos = getNodeSectorPos(nodepos); 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 // Get sector
m_env.getMap().emergeSector(sectorpos); m_env.getMap().emergeSector(sectorpos);
// Get ground height at point // Get ground height at point
groundheight = m_env.getMap().getGroundHeight(nodepos, true); groundheight = m_env.getServerMap().findGroundLevel(nodepos);
// The sector should have been generated -> groundheight exists
assert(groundheight > GROUNDHEIGHT_VALID_MINVALUE);
// Don't go underwater // Don't go underwater
if(groundheight < WATER_LEVEL) if(groundheight < WATER_LEVEL)
{ {
@ -3384,10 +3452,9 @@ Player *Server::emergePlayer(const char *name, const char *password,
player->setPosition(intToFloat(v3s16( player->setPosition(intToFloat(v3s16(
nodepos.X, nodepos.X,
//groundheight + 1, groundheight + 1,
groundheight + 15,
nodepos.Y nodepos.Y
))); ), BS));
#endif #endif
/* /*

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string> #include <string>
#include "utility.h" #include "utility.h"
#include "porting.h" #include "porting.h"
#include "map.h"
struct QueuedBlockEmerge struct QueuedBlockEmerge
{ {
@ -341,12 +342,13 @@ private:
u32 m_excess_gotblocks; u32 m_excess_gotblocks;
}; };
class Server : public con::PeerHandler class Server : public con::PeerHandler, public MapEventReceiver
{ {
public: public:
/* /*
NOTE: Every public method should be thread-safe NOTE: Every public method should be thread-safe
*/ */
Server( Server(
std::string mapsavedir std::string mapsavedir
); );
@ -361,23 +363,11 @@ public:
void Receive(); void Receive();
void ProcessData(u8 *data, u32 datasize, u16 peer_id); 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(); core::list<PlayerInfo> getPlayerInfo();
u32 getDayNightRatio() u32 getDayNightRatio()
{ {
s32 d = 8; return time_to_daynight_ratio(m_time_of_day.get());
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;
} }
bool getShutdownRequested() bool getShutdownRequested()
@ -385,6 +375,13 @@ public:
return m_shutdown_requested.get(); 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: private:
// Virtual methods from con::PeerHandler. // Virtual methods from con::PeerHandler.
@ -398,6 +395,11 @@ private:
void SendInventory(u16 peer_id); void SendInventory(u16 peer_id);
void SendChatMessage(u16 peer_id, const std::wstring &message); void SendChatMessage(u16 peer_id, const std::wstring &message);
void BroadcastChatMessage(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 // Sends blocks to clients
void SendBlocks(float dtime); void SendBlocks(float dtime);
@ -485,6 +487,24 @@ private:
MutexedVariable<bool> m_shutdown_requested; 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 EmergeThread;
friend class RemoteClient; 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: Callbacks in script:
step(self, dtime) on_step(self, dtime)
get_client_init_data(self) on_get_client_init_data(self)
get_server_init_data(self) on_get_server_init_data(self)
initialize(self, data) 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) static int lf_object_set_base_position(lua_State *L)
{ {
// 4: z // 2: position
lua_Number z = lua_tonumber(L, -1); 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_pop(L, 1);
// 3: y lua_pushstring(L, "Y");
lua_gettable(L, -2);
lua_Number y = lua_tonumber(L, -1); lua_Number y = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
// 2: x lua_pushstring(L, "Z");
lua_Number x = lua_tonumber(L, -1); lua_gettable(L, -2);
lua_Number z = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pop(L, 1); lua_pop(L, 1);
// 1: self // 1: self
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1); 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) 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(); v3f pos = self->getBasePosition();
lua_newtable(L);
lua_pushstring(L, "X");
lua_pushnumber(L, pos.X/BS); lua_pushnumber(L, pos.X/BS);
lua_settable(L, -3);
lua_pushstring(L, "Y");
lua_pushnumber(L, pos.Y/BS); lua_pushnumber(L, pos.Y/BS);
lua_settable(L, -3);
lua_pushstring(L, "Z");
lua_pushnumber(L, pos.Z/BS); 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) static int lf_object_get_node(lua_State *L)
{ {
// 4: z // 2: position
lua_Number z = lua_tonumber(L, -1); 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_pop(L, 1);
// 3: y lua_pushstring(L, "Y");
lua_gettable(L, -2);
lua_Number y = lua_tonumber(L, -1); lua_Number y = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
// 2: x lua_pushstring(L, "Z");
lua_Number x = lua_tonumber(L, -1); lua_gettable(L, -2);
lua_Number z = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pop(L, 1); lua_pop(L, 1);
// 1: self // 1: self
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1); 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_pushstring(L, "param2");
lua_pushinteger(L, n.param2); lua_pushinteger(L, n.param2);
lua_settable(L, -3); lua_settable(L, -3);
lua_pushstring(L, "walkable");
lua_pushboolean(L, content_features(n.d).walkable);
lua_settable(L, -3);
// Return the table // Return the table
return 1; return 1;
@ -227,39 +239,82 @@ static int lf_object_get_node(lua_State *L)
#if 0 #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; MapNode n;
// 5: n // 1: node
// Get fields of table assert(lua_istable(L, -1));
lua_pushstring(L, "content");
lua_pushinteger(L, "content");
lua_gettable(L, -2); lua_gettable(L, -2);
n.d = lua_tonumber(L, -1); n.d = lua_tointeger(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
lua_pushstring(L, "param1");
lua_pushinteger(L, "param1");
lua_gettable(L, -2); lua_gettable(L, -2);
n.param = lua_tonumber(L, -1); n.param = lua_tointeger(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
lua_pushstring(L, "param2");
lua_pushinteger(L, "param2");
lua_gettable(L, -2); lua_gettable(L, -2);
n.param2 = lua_tonumber(L, -1); n.param2 = lua_tointeger(L, -1);
lua_pop(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); 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); lua_pop(L, 1);
// 3: y lua_pushstring(L, "Y");
lua_gettable(L, -2);
lua_Number y = lua_tonumber(L, -1); lua_Number y = lua_tonumber(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
// 2: x lua_pushstring(L, "Z");
lua_Number x = lua_tonumber(L, -1); lua_gettable(L, -2);
lua_Number z = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_pop(L, 1); lua_pop(L, 1);
// 1: self // 1: self
LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1); 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); 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 lua_pushboolean(L, succeeded);
MapNode n(CONTENT_IGNORE); return 1;
n = self->getEnv()->getMap().getNodeNoEx(pos); }
// Create a table with some data about the node /*
lua_newtable(L); bool object_place_node(self, {X=,Y=,Z=}, node)
lua_pushstring(L, "content"); node={content=,param1=,param2=}
lua_pushinteger(L, n.d); param1 and param2 are optional
lua_settable(L, -3); Return true on success
lua_pushstring(L, "walkable"); */
lua_pushboolean(L, content_features(n.d).walkable); static int lf_object_place_node(lua_State *L)
lua_settable(L, -3); {
// 3: node
// Return the table 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; return 1;
} }
#endif
/* /*
object_remove(x,y,z) object_remove(x,y,z)
@ -306,6 +414,38 @@ static int lf_object_remove(lua_State *L)
return 0; 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): LuaSAO::LuaSAO(ServerEnvironment *env, u16 id, v3f pos):
ServerActiveObject(env, id, pos), ServerActiveObject(env, id, pos),
L(NULL) 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_get_base_position", lf_object_get_base_position);
lua_register(L, "object_add_message", lf_object_add_message); lua_register(L, "object_add_message", lf_object_add_message);
lua_register(L, "object_get_node", lf_object_get_node); 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); lua_register(L, "object_remove", lf_object_remove);
} }
@ -372,7 +515,7 @@ std::string LuaSAO::getClientInitializationData()
do{ do{
const char *funcname = "get_client_init_data"; const char *funcname = "on_get_client_init_data";
lua_getglobal(L, funcname); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) if(!lua_isfunction(L,-1))
{ {
@ -430,7 +573,7 @@ std::string LuaSAO::getServerInitializationData()
do{ do{
const char *funcname = "get_server_init_data"; const char *funcname = "on_get_server_init_data";
lua_getglobal(L, funcname); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) if(!lua_isfunction(L,-1))
{ {
@ -474,7 +617,41 @@ std::string LuaSAO::getServerInitializationData()
return data; 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::istringstream is(data, std::ios::binary);
std::string script_name = deSerializeString(is); std::string script_name = deSerializeString(is);
@ -483,10 +660,10 @@ void LuaSAO::initialize(const std::string &data)
loadScripts(script_name); 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); lua_getglobal(L, funcname);
if(!lua_isfunction(L,-1)) 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) 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)) if(!lua_isfunction(L,-1))
{ {
lua_pop(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; return;
} }
@ -565,7 +744,8 @@ void LuaSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
// Call (2 parameters, 0 result) // Call (2 parameters, 0 result)
if(lua_pcall(L, 2, 0, 0)) 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; <<lua_tostring(L,-1)<<std::endl;
return; return;
} }

@ -131,10 +131,10 @@ public:
} }
virtual std::string getClientInitializationData(); virtual std::string getClientInitializationData();
virtual std::string getServerInitializationData(); 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); void loadScripts(const std::string &script_name);

@ -1832,7 +1832,11 @@ inline std::string deSerializeString(std::istream &is)
{ {
char buf[2]; char buf[2];
is.read(buf, 2); is.read(buf, 2);
if(is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
u16 s_size = readU16((u8*)buf); u16 s_size = readU16((u8*)buf);
if(s_size == 0)
return "";
Buffer<char> buf2(s_size); Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size); is.read(&buf2[0], s_size);
std::string s; std::string s;
@ -1867,7 +1871,11 @@ inline std::string deSerializeLongString(std::istream &is)
{ {
char buf[4]; char buf[4];
is.read(buf, 4); is.read(buf, 4);
if(is.gcount() != 4)
throw SerializationError("deSerializeLongString: size not read");
u32 s_size = readU32((u8*)buf); u32 s_size = readU32((u8*)buf);
if(s_size == 0)
return "";
Buffer<char> buf2(s_size); Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size); is.read(&buf2[0], s_size);
std::string s; std::string s;