mainly work on object scripting api
authorPerttu Ahola <celeron55@gmail.com>
Wed, 23 Feb 2011 00:49:57 +0000 (02:49 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Wed, 23 Feb 2011 00:49:57 +0000 (02:49 +0200)
19 files changed:
data/luaobjects/test/client.lua
data/luaobjects/test/server.lua
minetest.conf.example
src/client.cpp
src/clientobject.cpp
src/defaultsettings.cpp
src/environment.cpp
src/environment.h
src/main.cpp
src/map.cpp
src/map.h
src/mapblockobject.cpp
src/player.cpp
src/porting.cpp
src/server.cpp
src/server.h
src/serverobject.cpp
src/serverobject.h
src/utility.h

index 80f085190b6dfebd8f5acc7a61630e1d031a04bb..6c5003959e8b1e163bcbc4faad3a8f1342d0e3cd 100644 (file)
@@ -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 = {
index ac7b4800d8e147247078bc2791f38f94ee0875b8..e79d277acd0a403ad0dfcc4c147de01b0ecd7d28 100644 (file)
@@ -1,25 +1,43 @@
 -- 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
+               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
        -- is in action
        if dtime > 0.5 then
@@ -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
 
index eb17ebc741a72c1aca585359c7721138ba45e8e8..c78266e7bb0353139decd210eb95646882f703d9 100644 (file)
@@ -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
 # 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
 
index 4c3992e6ac720bf51bd48415197be007012859b6..3f0457e6e5af78c6d8b5bbca392f5140ef4037d3 100644 (file)
@@ -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;
-
-                       dstream<<"time_of_day="<<m_time_of_day.get()
-                                       <<", t="<<t
+                       u32 dr = time_to_daynight_ratio(m_time_of_day.get());
+
+                       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;
 }
index bb4497e94ad23c4ffe39e5755fa8d9bc21fe28b3..4319f1ef0a7966db38555685d9c0121a724ec7fa 100644 (file)
@@ -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))
        {
index a456a4fceee1a7bc4a1471b927d6e6ae92cd8bb2..4a96f82331f8247523738a20f4b876ec9ba4bbe6 100644 (file)
@@ -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");
index 179e813ae977fc32d0eeb4b71c199811b95ced5e..ef973fb1e68dbe91bedd3c95b497a732549c00b2 100644 (file)
@@ -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,35 +468,55 @@ 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);
-               u32 selected_i = myrand_range(0, dirlist.size()-1);
-               std::string selected_name = "";
-               selected_name = dirlist[selected_i].name;
-               /*for(u32 i=0; i<dirlist.size(); i++)*/
-               
-               dstream<<"ServerEnvironment: Selected script name \""<<selected_name
-                               <<"\" for new lua object"<<std::endl;
+               if(dirlist.size() > 0)
+               {
+                       u32 selected_i = myrand_range(0, dirlist.size()-1);
+                       std::string selected_name = "";
+                       selected_name = dirlist[selected_i].name;
+                       /*for(u32 i=0; i<dirlist.size(); i++)*/
+                       
+                       dstream<<"ServerEnvironment: Selected script name \""<<selected_name
+                                       <<"\" for new lua object"<<std::endl;
 
-               /*
-                       Load the scripts for the type
-               */
-               obj->loadScripts(selected_name.c_str());
+                       /*
+                               Load the scripts for the type
+                       */
+                       obj->initializeFromNothing(selected_name.c_str());
 
-               // Add the object to the environment
-               addActiveObject(obj);
+                       // Add the object to the environment
+                       addActiveObject(obj);
+               }
        }
+
+       } // enable_experimental
 }
 
 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
index 124bf9aab934d5f9dd7a1cb694726d10b340c0f7..a476230aabef91a3bef85bc18f9ef43f1b713a59 100644 (file)
@@ -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;
index 06fae74bf5c532ae13bb867b0fd1ba5f9ccf75f0..95761cf96db476fff4f44e614619a10f7db89212 100644 (file)
@@ -199,15 +199,6 @@ FIXME: Server went into some infinite PeerNotFoundException loop
 Objects:\r
 --------\r
 \r
-TODO: Better handling of objects and mobs\r
-      - Scripting?\r
-      - There has to be some way to do it with less messy code\r
-         - Make separate classes for client and server\r
-           - Client should not discriminate between blocks, server should\r
-           - Make other players utilize the same framework\r
-               - This is also needed for objects that don't get sent to client\r
-                 but are used for triggers etc\r
-\r
 TODO: There has to be some better way to handle static objects than to\r
       send them all the time. This affects signs and item objects.\r
 SUGG: Signs could be done in the same way as torches. For this, blocks\r
@@ -262,6 +253,7 @@ Doing now (most important at the top):
 * not done\r
 \r
 === Fixmes\r
+* Check the fixmes in the list above\r
 * Make server find the spawning place from the real map data, not from\r
   the heightmap\r
   - But the changing borders of chunk have to be avoided, because\r
@@ -269,13 +261,16 @@ Doing now (most important at the top):
 * Make the generator to run in background and not blocking block\r
   placement and transfer\r
 * only_from_disk might not work anymore - check and fix it.\r
-* Check the fixmes in the list above\r
 \r
 === Making it more portable\r
 * Some MSVC: std::sto* are defined without a namespace and collide\r
   with the ones in utility.h\r
 \r
 === Features\r
+* Map should make the appropriate MapEditEvents\r
+* Add a global Lua spawn handler and such\r
+* Get rid of MapBlockObjects\r
+* Other players could be sent to clients as LuaCAOs\r
 * Add mud underground\r
 * Make an "environment metafile" to store at least time of day\r
 * Move digging property stuff from material.{h,cpp} to mapnode.cpp...\r
@@ -283,17 +278,15 @@ Doing now (most important at the top):
 * Add some kind of erosion and other stuff that now is possible\r
 * Make client to fetch stuff asynchronously\r
   - Needs method SyncProcessData\r
-* Fix the problem with the server constantly saving one or a few\r
-  blocks? List the first saved block, maybe it explains.\r
-  - It is probably caused by oscillating water\r
-* Water doesn't start flowing after map generation like it should\r
-  - Are there still problems?\r
 * Better water generation (spread it to underwater caverns but don't\r
   fill dungeons that don't touch big water masses)\r
 * When generating a chunk and the neighboring chunk doesn't have mud\r
   and stuff yet and the ground is fairly flat, the mud will flow to\r
   the other chunk making nasty straight walls when the other chunk\r
   is generated. Fix it.\r
+* Fix the problem with the server constantly saving one or a few\r
+  blocks? List the first saved block, maybe it explains.\r
+  - It is probably caused by oscillating water\r
 * Make a small history check to transformLiquids to detect and log\r
   continuous oscillations, in such detail that they can be fixed.\r
 * Combine meshes to bigger ones in ClientMap and set them EHM_STATIC\r
@@ -1796,7 +1789,7 @@ int main(int argc, char *argv[])
                \r
                dstream<<"Created main menu"<<std::endl;\r
 \r
-               while(g_device->run())\r
+               while(g_device->run() && kill == false)\r
                {\r
                        // Run global IrrlichtWrapper's main thread processing stuff\r
                        g_irrlicht->Run();\r
@@ -1811,7 +1804,7 @@ int main(int argc, char *argv[])
                }\r
                \r
                // Break out of menu-game loop to shut down cleanly\r
-               if(g_device->run() == false)\r
+               if(g_device->run() == false || kill == true)\r
                        break;\r
                \r
                dstream<<"Dropping main menu"<<std::endl;\r
index c92039664dca79cf2fdfb2297f731ae9c5e5de02..8e71212b0fe6bc1ed29d1e984cb15443bf97fddc 100644 (file)
@@ -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);
@@ -866,7 +847,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        /*PrintInfo(m_dout);
        m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
                        <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
+       
        /*
                From this node to nodes underneath:
                If lighting is sunlight (1.0), unlight neighbours and
@@ -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_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_camera_mutex.Init();
+       assert(m_camera_mutex.IsInitialized());
+       
        m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
                        BS*1000000,BS*1000000,BS*1000000);
-       
-       //setPosition(v3f(BS,BS,BS));
 }
 
 ClientMap::~ClientMap()
index d39ede26f54e6df59aac6e97e86eeddf0a7676e6..1cebef63442727eac60cfdb9dd02a42a07e8af08 100644 (file)
--- a/src/map.h
+++ b/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,25 +114,11 @@ public:
                delete this;
        }
 
-       void updateCamera(v3f pos, v3f dir)
-       {
-               JMutexAutoLock lock(m_camera_mutex);
-               m_camera_position = pos;
-               m_camera_direction = dir;
-       }
+       void addEventReceiver(MapEventReceiver *event_receiver);
+       void removeEventReceiver(MapEventReceiver *event_receiver);
+       // event shall be deleted by caller after the call.
+       void dispatchEvent(MapEditEvent *event);
 
-       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
        MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
        // On failure returns NULL
@@ -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);
        
@@ -211,6 +238,14 @@ public:
                        core::map<v3s16, MapBlock*> &modified_blocks);
        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;
@@ -444,6 +478,9 @@ public:
                        core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
        );
 #endif
+       
+       // Helper for placing objects on ground level
+       s16 findGroundLevel(v2s16 p2d);
 
        /*
                Misc. helper functions for fiddling with directory and file
@@ -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
index ff58fd04555a4e29c0a24ce65d26ea0fee12067e..cd1618c3a60410960cc7aa99e82011465f720297 100644 (file)
@@ -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(
index 07879c21adafdcfb113cef2a865ce4b2d23fd076..3bde8a56324f0bf193ab4c8a90ca29cc87f01d4b 100644 (file)
@@ -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.
index 97b02823668d24545e7f34edd4df4fa359b943cc..e8b1352558bb7737399e79513177182f7361cde2 100644 (file)
@@ -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();
 
index 9f6b755fbde76d07562142192b5dcf916587471b..76fa23a932307f78bd35f887e86c822dfd9f4840 100644 (file)
@@ -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,12 +1194,18 @@ void Server::AsyncRunStep()
                }
        }
 
+       if(g_settings.getBool("enable_experimental"))
+       {
+
        /*
                Check added and deleted active objects
        */
        {
                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();
@@ -1204,8 +1213,9 @@ void Server::AsyncRunStep()
                {
                        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;
                }
                
                /*
@@ -2150,17 +2147,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                n.d = mitem->getMaterial();
                                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
 
                /*
index 292e2d5cd84affc902812010e3fb272b079c903e..f997828b2a20276cfce58f95366090a218a8694c 100644 (file)
@@ -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,29 +363,24 @@ 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()
        {
                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:
 
@@ -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);
@@ -484,6 +486,24 @@ private:
        std::string m_mapsavedir;
 
        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;
index 8b41cef1684456d1861691439e87423e13c699cf..7266fc2b1332e0062b7b0129ffeca684f35ef3b8 100644 (file)
@@ -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);
@@ -268,27 +323,80 @@ static int lf_object_set_node(lua_State *L)
        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().removeNodeWithEvent(pos);
+       
+       lua_pushboolean(L, succeeded);
+       return 1;
+}
 
-       /*dstream<<"Checking node from pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z
-                       <<")"<<std::endl;*/
+/*
+       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);
        
-       // Get the node
-       MapNode n(CONTENT_IGNORE);
-       n = self->getEnv()->getMap().getNodeNoEx(pos);
+       assert(self);
 
-       // 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);
+       v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
        
-       // Return the table
+       /*
+               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;
        }
index 050b3a02f4354f3fdc9d4ac406c992515227da94..557d9b6186e13a870b89f4c44d59731f102d015b 100644 (file)
@@ -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);
 
index d4bb04c6c948a0a593ca2ca85f11a726490d7fbd..2b878b82b6a3c6b7af83d9c31a1c004acace8ee1 100644 (file)
@@ -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;