summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2010-11-27 01:02:21 +0200
committerPerttu Ahola <celeron55@gmail.com>2010-11-27 01:02:21 +0200
commit4e249fb3fbf75f0359758760d88e22aa5b14533c (patch)
tree323087d05efbd2ace27b316d4f017cf812a31992 /src/main.cpp
downloadminetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.gz
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.bz2
minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.zip
Initial files
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp2339
1 files changed, 2339 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 000000000..971e0eac3
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,2339 @@
+/*
+(c) 2010 Perttu Ahola <celeron55@gmail.com>
+
+Minetest
+
+NOTE: VBO cannot be turned on for fast-changing stuff because there
+ is an apparanet memory leak in irrlicht when using it
+
+SUGGESTION: add a second lighting value to the MS nibble of param of
+ air to tell how bright the air node is when there is no sunlight.
+ When day changes to night, these two values can be interpolated.
+TODO: Fix address to be ipv6 compatible
+
+TODO: ESC Pause mode in which the cursor is not kept at the center of window.
+TODO: Stop player if focus of window is taken away (go to pause mode)
+TODO: Optimize and fix makeFastFace or whatever it's called
+ - Face calculation is the source of CPU usage on the client
+SUGGESTION: The client will calculate and send lighting changes and
+ the server will randomly check some of them and kick the client out
+ if it fails to calculate them right.
+ - Actually, it could just start ignoring them and calculate them
+ itself.
+SUGGESTION: Combine MapBlock's face caches to so big pieces that VBO
+ gets used
+ - That is >500 vertices
+
+TODO: Better dungeons
+TODO: There should be very slight natural caves also, starting from
+ only a straightened-up cliff
+
+TODO: Changing of block with mouse wheel or something
+TODO: Menus
+
+TODO: Mobs
+ - Server:
+ - One single map container with ids as keys
+ - Client:
+ - ?
+TODO: - Keep track of the place of the mob in the last few hundreth's
+ of a second - then, if a player hits it, take the value that is
+ avg_rtt/2 before the moment the packet is received.
+TODO: - Scripting
+
+SUGGESTION: Modify client to calculate single changes asynchronously
+
+TODO: Moving players more smoothly. Calculate moving animation from
+ data sent by server.
+
+TODO: There are some lighting-related todos and fixmes in
+ ServerMap::emergeBlock
+
+TODO: Make a dirt node and use it under water
+
+FIXME: When a new sector is generated, it may change the ground level
+ of it's and it's neighbors border that two blocks that are
+ above and below each other and that are generated before and
+ after the sector heightmap generation (order doesn't matter),
+ can have a small gap between each other at the border.
+SUGGESTION: Use same technique for sector heightmaps as what we're
+ using for UnlimitedHeightmap? (getting all neighbors
+ when generating)
+
+TODO: Set server to automatically find a good spawning place in some
+ place where there is water and land.
+ - Map to have a getWalkableNear(p)
+
+TODO: Transfer more blocks in a single packet
+SUGG: A blockdata combiner class, to which blocks are added and at
+ destruction it sends all the stuff in as few packets as possible.
+
+TODO: If player is on ground, mainly fetch ground-level blocks
+TODO: Fetch stuff mainly from the viewing direction
+
+TODO: Expose Connection's seqnums and ACKs to server and client.
+ - This enables saving many packets and making a faster connection
+ - This also enables server to check if client has received the
+ most recent block sent, for example.
+
+SUGG: Add a time value to the param of footstepped grass and check it
+ against a global timer when a block is accessed, to make old
+ steps fade away.
+
+FIXME: There still are *some* tiny glitches in lighting as seen from
+ the client side. The server calculates them right but sometimes
+ they don't get transferred properly.
+ - Server probably checks that a block is not sent, then continues
+ to sending it, then the emerge thread marks it as unsent and then
+ the sender sends the block as it was before emerging?
+TODO: How about adding a "revision" field to MapBlocks?
+
+TODO: More fine-grained control of client's dumping of blocks from
+ memory
+
+TODO: Somehow prioritize the sending of blocks and combine the block
+ send queue lengths
+ - Take two blocks to be sent next from each client and assign
+ a priority value to them
+ - Priority is the same as distance from player
+ - Take the highest priority ones and send them. Send as many as
+ fits in the global send queue maximum length (sum of lengths
+ of client queues)
+TODO: Make the amount of blocks sending to client and the total
+ amount of blocks dynamically limited. Transferring blocks is the
+ main network eater of this system, so it is the one that has
+ to be throttled so that RTTs stay low.
+FIXME: There is a bug that sometimes the EmergeThread bumps to
+ the client's emerge counter being already 0, and also the
+ sending queue size of the client can float to 1 or 2, which
+ stops the map from loading at all.
+ - A quick hack could be applied to ignore the error of
+ being at 0 and timing out old entries
+SUGG: Make client send GOTBLOCKS before updating meshes
+
+TODO: Server to load starting inventory from disk
+
+NOTE: iostream.imbue(std::locale("C")) is very slow
+NOTE: Global locale is now set at initialization
+
+TODO: PLayers to only be hidden when the client quits.
+TODO: - Players to be saved on disk, with inventory
+TODO: Players to be saved as text in map/players/<name>
+
+SUGGESTION: A map editing mode (similar to dedicated server mode)
+
+TODO: Maybe: Create a face calculation queue on the client that is
+ processed in a separate thread
+TODO: Make client's mesh updates to happen in a thread similar to
+ server's EmergeThread.
+ - This is not really needed, mesh update is really fast
+ - Instead, the lighting update can be slow
+ - So, this todo is not really a todo. It is a not-todo.
+SUGG: Make server to send all modified blocks after a node change
+ after all the stuff including lighting have been updated
+
+TODO: Make fetching sector's blocks more efficient when rendering
+ sectors that have very large amounts of blocks (on client)
+
+TODO: Make the video backend selectable
+
+TODO: A timestamp to blocks
+
+TODO: Client side:
+ - The server sends all active objects of the active blocks
+ at constant intervals. They should fit in a few packets.
+ - The client keeps track of what blocks at the moment are
+ having active objects in them.
+ - All blocks going in and out of the active buffer are recorded.
+ - For outgoing blocks, objects are removed from the blocks
+ and from the scene
+ - For incoming blocks, objects are added to the blocks and
+ to the scene.
+
+TODO: Server side:
+ - A "near blocks" buffer, in which some nearby blocks are stored.
+ - For all blocks in the buffer, objects are stepped(). This
+ means they are active.
+ - All blocks going in and out of the buffer are recorded.
+ - For outgoing blocks, a timestamp is written.
+ - For incoming blocks, the time difference is calculated and
+ objects are stepped according to it.
+
+TODO: Add config parameters for server's sending and generating distance
+
+TODO: Make amount of trees and other plants configurable
+ - Save to a metafile
+
+TODO: Copy the text of the last picked sign to inventory in creative
+ mode
+
+TODO: Untie client network operations from framerate
+
+TODO: Make a copy of close-range environment on client for showing
+ on screen, with minimal mutexes to slow the main loop down
+
+TODO: Make a PACKET_COMBINED which contains many subpackets. Utilize
+ it by sending more stuff in a single packet.
+
+Doing now:
+======================================================================
+
+
+======================================================================
+
+*/
+
+/*
+ Setting this to 1 enables a special camera mode that forces
+ the renderers to think that the camera statically points from
+ the starting place to a static direction.
+
+ This allows one to move around with the player and see what
+ is actually drawn behind solid things etc.
+*/
+#define FIELD_OF_VIEW_TEST 0
+
+#ifdef UNITTEST_DISABLE
+ #ifdef _WIN32
+ #pragma message ("Disabling unit tests")
+ #else
+ #warning "Disabling unit tests"
+ #endif
+ // Disable unit tests
+ #define ENABLE_TESTS 0
+#else
+ // Enable unit tests
+ #define ENABLE_TESTS 1
+#endif
+
+#ifdef _MSC_VER
+#pragma comment(lib, "Irrlicht.lib")
+#pragma comment(lib, "jthread.lib")
+// This would get rid of the console window
+//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
+#endif
+
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #define sleep_ms(x) Sleep(x)
+#else
+ #include <unistd.h>
+ #define sleep_ms(x) usleep(x*1000)
+#endif
+
+#include <iostream>
+#include <fstream>
+#include <time.h>
+#include <jmutexautolock.h>
+#include "common_irrlicht.h"
+#include "debug.h"
+#include "map.h"
+#include "player.h"
+#include "main.h"
+#include "test.h"
+#include "environment.h"
+#include "server.h"
+#include "client.h"
+#include "serialization.h"
+#include "constants.h"
+#include "strfnd.h"
+#include "porting.h"
+#include <locale.h>
+
+IrrlichtDevice *g_device = NULL;
+
+const char *g_material_filenames[MATERIALS_COUNT] =
+{
+ "../data/stone.png",
+ "../data/grass.png",
+ "../data/water.png",
+ "../data/light.png",
+ "../data/tree.png",
+ "../data/leaves.png",
+ "../data/grass_footsteps.png",
+ "../data/mese.png"
+};
+
+video::SMaterial g_materials[MATERIALS_COUNT];
+//video::SMaterial g_mesh_materials[3];
+
+// All range-related stuff below is locked behind this
+JMutex g_range_mutex;
+
+// Blocks are generated in this range from the player
+// This is limited vertically to half by Client::fetchBlocks()
+s16 g_forcedfetch_range_nodes = FORCEDFETCH_RANGE;
+
+// Blocks are viewed in this range from the player
+s16 g_viewing_range_nodes = 60;
+
+// This is updated by the client's fetchBlocks routine
+//s16 g_actual_viewing_range_nodes = VIEWING_RANGE_NODES_DEFAULT;
+
+// If true, the preceding value has no meaning and all blocks
+// already existing in memory are drawn
+bool g_viewing_range_all = false;
+
+// This is the freetime ratio imposed by the dynamic viewing
+// range changing code.
+// It is controlled by the main loop to the smallest value that
+// inhibits glitches (dtime jitter) in the main loop.
+//float g_freetime_ratio = FREETIME_RATIO_MAX;
+
+
+/*
+ Settings.
+ These are loaded from the config file.
+*/
+
+std::string g_dedicated_server;
+
+// Client stuff
+float g_wanted_fps = FPS_DEFAULT_WANTED;
+float g_fps_max = FPS_DEFAULT_MAX;
+s16 g_viewing_range_nodes_max = 300;
+s16 g_viewing_range_nodes_min = 20;
+std::string g_screenW;
+std::string g_screenH;
+std::string g_host_game;
+std::string g_port;
+std::string g_address;
+std::string g_name;
+bool g_random_input = false;
+float g_client_delete_unused_sectors_timeout = 1200;
+
+// Server stuff
+bool g_creative_mode = false;
+MapgenParams g_mapgen_params;
+
+/*
+ Random stuff
+*/
+
+//u16 g_selected_material = 0;
+u16 g_selected_item = 0;
+
+bool g_esc_pressed = false;
+
+std::wstring g_text_buffer;
+bool g_text_buffer_accepted = false;
+
+// When true, the mouse and keyboard are grabbed
+bool g_game_focused = true;
+
+/*
+ Debug streams
+*/
+
+// Connection
+std::ostream *dout_con_ptr = &dummyout;
+std::ostream *derr_con_ptr = &dstream_no_stderr;
+//std::ostream *dout_con_ptr = &dstream_no_stderr;
+//std::ostream *derr_con_ptr = &dstream_no_stderr;
+//std::ostream *dout_con_ptr = &dstream;
+//std::ostream *derr_con_ptr = &dstream;
+
+// Server
+std::ostream *dout_server_ptr = &dstream;
+std::ostream *derr_server_ptr = &dstream;
+
+// Client
+std::ostream *dout_client_ptr = &dstream;
+std::ostream *derr_client_ptr = &dstream;
+
+/*
+ Config stuff
+*/
+
+// Returns false on EOF
+bool parseConfigObject(std::istream &is)
+{
+ // float g_wanted_fps
+ // s16 g_viewing_range_nodes_max
+
+ if(is.eof())
+ return false;
+
+ std::string line;
+ std::getline(is, line);
+ //dstream<<"got line: \""<<line<<"\""<<std::endl;
+
+ std::string trimmedline = trim(line);
+
+ // Ignore comments
+ if(trimmedline[0] == '#')
+ return true;
+
+ //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
+
+ Strfnd sf(trim(line));
+
+ std::string name = sf.next("=");
+ name = trim(name);
+
+ if(name == "")
+ return true;
+
+ std::string value = sf.next("\n");
+ value = trim(value);
+
+ dstream<<"Config name=\""<<name<<"\" value=\""
+ <<value<<"\""<<std::endl;
+
+ if(name == "dedicated_server")
+ g_dedicated_server = value;
+
+ // Client stuff
+ else if(name == "wanted_fps")
+ {
+ g_wanted_fps = atof(value.c_str());
+ }
+ else if(name == "fps_max")
+ {
+ g_fps_max = atof(value.c_str());
+ }
+ else if(name == "viewing_range_nodes_max")
+ {
+ s32 v = atoi(value.c_str());
+ if(v < 0)
+ v = 0;
+ if(v > 32767)
+ v = 32767;
+ g_viewing_range_nodes_max = v;
+ }
+ else if(name == "viewing_range_nodes_min")
+ {
+ s32 v = atoi(value.c_str());
+ if(v < 0)
+ v = 0;
+ if(v > 32767)
+ v = 32767;
+ g_viewing_range_nodes_min = v;
+ }
+ else if(name=="screenW")
+ g_screenW = value;
+ else if(name=="screenH")
+ g_screenH = value;
+ else if(name == "host_game")
+ g_host_game = value;
+ else if(name == "port")
+ g_port = value;
+ else if(name == "address")
+ g_address = value;
+ else if(name == "name")
+ g_name = value;
+ else if(name == "random_input")
+ g_random_input = is_yes(value);
+ else if(name == "client_delete_unused_sectors_timeout")
+ {
+ std::istringstream vis(value);
+ //vis.imbue(std::locale("C"));
+ vis>>g_client_delete_unused_sectors_timeout;
+ }
+
+ // Server stuff
+ else if(name == "creative_mode")
+ g_creative_mode = is_yes(value);
+ else if(name == "mapgen_heightmap_blocksize")
+ {
+ s32 d = atoi(value.c_str());
+ if(d > 0 && (d & (d-1)) == 0)
+ g_mapgen_params.heightmap_blocksize = d;
+ else
+ dstream<<"Invalid value in config file: \""
+ <<line<<"\""<<std::endl;
+ }
+ else if(name == "mapgen_height_randmax")
+ g_mapgen_params.height_randmax = value;
+ else if(name == "mapgen_height_randfactor")
+ g_mapgen_params.height_randfactor = value;
+ else if(name == "mapgen_height_base")
+ g_mapgen_params.height_base = value;
+ else if(name == "mapgen_plants_amount")
+ g_mapgen_params.plants_amount = value;
+
+ else
+ {
+ dstream<<"Unknown option in config file: \""
+ <<line<<"\""<<std::endl;
+ }
+
+ return true;
+}
+
+// Returns true on success
+bool readConfigFile(const char *filename)
+{
+ std::ifstream is(filename);
+ if(is.good() == false)
+ {
+ dstream<<DTIME<<"Error opening configuration file: "
+ <<filename<<std::endl;
+ return false;
+ }
+
+ dstream<<DTIME<<"Parsing configuration file: "
+ <<filename<<std::endl;
+
+ while(parseConfigObject(is));
+
+ return true;
+}
+
+/*
+ Timestamp stuff
+*/
+
+JMutex g_timestamp_mutex;
+//std::string g_timestamp;
+
+std::string getTimestamp()
+{
+ if(g_timestamp_mutex.IsInitialized()==false)
+ return "";
+ JMutexAutoLock lock(g_timestamp_mutex);
+ //return g_timestamp;
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+ char cs[20];
+ strftime(cs, 20, "%H:%M:%S", tm);
+ return cs;
+}
+
+class MyEventReceiver : public IEventReceiver
+{
+public:
+ // This is the one method that we have to implement
+ virtual bool OnEvent(const SEvent& event)
+ {
+ // Remember whether each key is down or up
+ if(event.EventType == irr::EET_KEY_INPUT_EVENT)
+ {
+ keyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
+
+ if(event.KeyInput.PressedDown)
+ {
+ //dstream<<"Pressed key: "<<(char)event.KeyInput.Key<<std::endl;
+ if(g_game_focused == false)
+ {
+ s16 key = event.KeyInput.Key;
+ if(key == irr::KEY_RETURN || key == irr::KEY_ESCAPE)
+ {
+ g_text_buffer_accepted = true;
+ }
+ else if(key == irr::KEY_BACK)
+ {
+ if(g_text_buffer.size() > 0)
+ g_text_buffer = g_text_buffer.substr
+ (0, g_text_buffer.size()-1);
+ }
+ else
+ {
+ wchar_t wc = event.KeyInput.Char;
+ if(wc != 0)
+ g_text_buffer += wc;
+ }
+ }
+
+ if(event.KeyInput.Key == irr::KEY_ESCAPE)
+ {
+ if(g_game_focused == true)
+ {
+ dstream<<DTIME<<"ESC pressed"<<std::endl;
+ g_esc_pressed = true;
+ }
+ }
+
+ // Material selection
+ if(event.KeyInput.Key == irr::KEY_KEY_F)
+ {
+ if(g_game_focused == true)
+ {
+ if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
+ g_selected_item++;
+ else
+ g_selected_item = 0;
+ dstream<<DTIME<<"Selected item: "
+ <<g_selected_item<<std::endl;
+ }
+ }
+
+ // Viewing range selection
+ if(event.KeyInput.Key == irr::KEY_KEY_R
+ && g_game_focused)
+ {
+ JMutexAutoLock lock(g_range_mutex);
+ if(g_viewing_range_all)
+ {
+ g_viewing_range_all = false;
+ dstream<<DTIME<<"Disabled full viewing range"<<std::endl;
+ }
+ else
+ {
+ g_viewing_range_all = true;
+ dstream<<DTIME<<"Enabled full viewing range"<<std::endl;
+ }
+ }
+ }
+ }
+
+ if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
+ {
+ if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
+ {
+ leftclicked = true;
+ }
+ if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
+ {
+ rightclicked = true;
+ }
+ if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
+ {
+ /*dstream<<"event.MouseInput.Wheel="
+ <<event.MouseInput.Wheel<<std::endl;*/
+ if(event.MouseInput.Wheel < 0)
+ {
+ if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
+ g_selected_item++;
+ else
+ g_selected_item = 0;
+ }
+ else if(event.MouseInput.Wheel > 0)
+ {
+ if(g_selected_item > 0)
+ g_selected_item--;
+ else
+ g_selected_item = PLAYER_INVENTORY_SIZE-1;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // This is used to check whether a key is being held down
+ virtual bool IsKeyDown(EKEY_CODE keyCode) const
+ {
+ return keyIsDown[keyCode];
+ }
+
+ MyEventReceiver()
+ {
+ for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
+ keyIsDown[i] = false;
+ leftclicked = false;
+ rightclicked = false;
+ }
+
+ bool leftclicked;
+ bool rightclicked;
+private:
+ // We use this array to store the current state of each key
+ bool keyIsDown[KEY_KEY_CODES_COUNT];
+ //s32 mouseX;
+ //s32 mouseY;
+};
+
+class InputHandler
+{
+public:
+ InputHandler()
+ {
+ }
+ virtual ~InputHandler()
+ {
+ }
+ virtual bool isKeyDown(EKEY_CODE keyCode) = 0;
+ virtual v2s32 getMousePos() = 0;
+ virtual void setMousePos(s32 x, s32 y) = 0;
+ virtual bool getLeftClicked() = 0;
+ virtual bool getRightClicked() = 0;
+ virtual void resetLeftClicked() = 0;
+ virtual void resetRightClicked() = 0;
+
+ virtual void step(float dtime) {};
+
+ virtual void clear() {};
+};
+
+InputHandler *g_input = NULL;
+
+void focusGame()
+{
+ g_input->clear();
+ g_game_focused = true;
+}
+
+void unFocusGame()
+{
+ g_game_focused = false;
+}
+
+class RealInputHandler : public InputHandler
+{
+public:
+ RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
+ m_device(device),
+ m_receiver(receiver)
+ {
+ }
+ virtual bool isKeyDown(EKEY_CODE keyCode)
+ {
+ return m_receiver->IsKeyDown(keyCode);
+ }
+ virtual v2s32 getMousePos()
+ {
+ return m_device->getCursorControl()->getPosition();
+ }
+ virtual void setMousePos(s32 x, s32 y)
+ {
+ m_device->getCursorControl()->setPosition(x, y);
+ }
+
+ virtual bool getLeftClicked()
+ {
+ if(g_game_focused == false)
+ return false;
+ return m_receiver->leftclicked;
+ }
+ virtual bool getRightClicked()
+ {
+ if(g_game_focused == false)
+ return false;
+ return m_receiver->rightclicked;
+ }
+ virtual void resetLeftClicked()
+ {
+ m_receiver->leftclicked = false;
+ }
+ virtual void resetRightClicked()
+ {
+ m_receiver->rightclicked = false;
+ }
+
+ void clear()
+ {
+ resetRightClicked();
+ resetLeftClicked();
+ }
+private:
+ IrrlichtDevice *m_device;
+ MyEventReceiver *m_receiver;
+};
+
+class RandomInputHandler : public InputHandler
+{
+public:
+ RandomInputHandler()
+ {
+ leftclicked = false;
+ rightclicked = false;
+ for(u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
+ keydown[i] = false;
+ }
+ virtual bool isKeyDown(EKEY_CODE keyCode)
+ {
+ return keydown[keyCode];
+ }
+ virtual v2s32 getMousePos()
+ {
+ return mousepos;
+ }
+ virtual void setMousePos(s32 x, s32 y)
+ {
+ mousepos = v2s32(x,y);
+ }
+
+ virtual bool getLeftClicked()
+ {
+ return leftclicked;
+ }
+ virtual bool getRightClicked()
+ {
+ return rightclicked;
+ }
+ virtual void resetLeftClicked()
+ {
+ leftclicked = false;
+ }
+ virtual void resetRightClicked()
+ {
+ rightclicked = false;
+ }
+
+ virtual void step(float dtime)
+ {
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1,10);
+ /*if(g_selected_material < USEFUL_MATERIAL_COUNT-1)
+ g_selected_material++;
+ else
+ g_selected_material = 0;*/
+ if(g_selected_item < PLAYER_INVENTORY_SIZE-1)
+ g_selected_item++;
+ else
+ g_selected_item = 0;
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 40);
+ keydown[irr::KEY_SPACE] = !keydown[irr::KEY_SPACE];
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 40);
+ keydown[irr::KEY_KEY_2] = !keydown[irr::KEY_KEY_2];
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 40);
+ keydown[irr::KEY_KEY_W] = !keydown[irr::KEY_KEY_W];
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 40);
+ keydown[irr::KEY_KEY_A] = !keydown[irr::KEY_KEY_A];
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 20);
+ mousespeed = v2s32(Rand(-20,20), Rand(-15,20));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 30);
+ leftclicked = true;
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if(counter1 < 0.0)
+ {
+ counter1 = 0.1*Rand(1, 20);
+ rightclicked = true;
+ }
+ }
+ mousepos += mousespeed;
+ }
+
+ s32 Rand(s32 min, s32 max)
+ {
+ return (rand()%(max-min+1))+min;
+ }
+private:
+ bool keydown[KEY_KEY_CODES_COUNT];
+ v2s32 mousepos;
+ v2s32 mousespeed;
+ bool leftclicked;
+ bool rightclicked;
+};
+
+void updateViewingRange(f32 frametime, Client *client)
+{
+ // Range_all messes up frametime_avg
+ if(g_viewing_range_all == true)
+ return;
+
+ // Initialize to the target value
+ static float frametime_avg = 1.0/g_wanted_fps;
+ frametime_avg = frametime_avg * 0.9 + frametime * 0.1;
+
+ static f32 counter = 0;
+ if(counter > 0){
+ counter -= frametime;
+ return;
+ }
+ //counter = 1.0; //seconds
+ counter = 0.5; //seconds
+
+ //float freetime_ratio = 0.2;
+ //float freetime_ratio = 0.4;
+ float freetime_ratio = FREETIME_RATIO;
+
+ float frametime_wanted = (1.0/(g_wanted_fps/(1.0-freetime_ratio)));
+
+ float fraction = sqrt(frametime_avg / frametime_wanted);
+
+ static bool fraction_is_good = false;
+
+ float fraction_good_threshold = 0.1;
+ float fraction_bad_threshold = 0.25;
+ float fraction_limit;
+ // Use high limit if fraction is good AND the fraction would
+ // lower the range. We want to keep the range fairly high.
+ if(fraction_is_good && fraction > 1.0)
+ fraction_limit = fraction_bad_threshold;
+ else
+ fraction_limit = fraction_good_threshold;
+
+ if(fabs(fraction - 1.0) < fraction_limit)
+ {
+ fraction_is_good = true;
+ return;
+ }
+ else
+ {
+ fraction_is_good = false;
+ }
+
+ //dstream<<"frametime_avg="<<frametime_avg<<std::endl;
+ //dstream<<"frametime_wanted="<<frametime_wanted<<std::endl;
+ /*dstream<<"fetching="<<client->isFetchingBlocks()
+ <<" faction = "<<fraction<<std::endl;*/
+
+ JMutexAutoLock lock(g_range_mutex);
+
+ s16 n = (float)g_viewing_range_nodes / fraction;
+ if(n < g_viewing_range_nodes_min)
+ n = g_viewing_range_nodes_min;
+ if(n > g_viewing_range_nodes_max)
+ n = g_viewing_range_nodes_max;
+
+ bool can_change = true;
+
+ if(client->isFetchingBlocks() == true && n > g_viewing_range_nodes)
+ can_change = false;
+
+ if(can_change)
+ g_viewing_range_nodes = n;
+
+ /*dstream<<"g_viewing_range_nodes = "
+ <<g_viewing_range_nodes<<std::endl;*/
+}
+
+class GUIQuickInventory : public IEventReceiver
+{
+public:
+ GUIQuickInventory(
+ gui::IGUIEnvironment* env,
+ gui::IGUIElement* parent,
+ v2s32 pos,
+ s32 itemcount,
+ Inventory *inventory):
+ m_itemcount(itemcount),
+ m_inventory(inventory)
+ {
+ core::rect<s32> imgsize(0,0,48,48);
+ core::rect<s32> textsize(0,0,48,16);
+ v2s32 spacing(0, 64);
+ for(s32 i=0; i<m_itemcount; i++)
+ {
+ m_images.push_back(env->addImage(
+ imgsize + pos + spacing*i
+ ));
+ m_images[i]->setScaleImage(true);
+ m_texts.push_back(env->addStaticText(
+ L"",
+ textsize + pos + spacing*i,
+ false, false
+ ));
+ m_texts[i]->setBackgroundColor(
+ video::SColor(128,0,0,0));
+ m_texts[i]->setTextAlignment(
+ gui::EGUIA_CENTER,
+ gui::EGUIA_UPPERLEFT);
+ }
+ }
+
+ virtual bool OnEvent(const SEvent& event)
+ {
+ return false;
+ }
+
+ void setSelection(s32 i)
+ {
+ m_selection = i;
+ }
+
+ void update()
+ {
+ s32 start = 0;
+
+ start = m_selection - m_itemcount / 2;
+
+ for(s32 i=0; i<m_itemcount; i++)
+ {
+ s32 j = i + start;
+
+ if(j > (s32)m_inventory->getSize() - 1)
+ j -= m_inventory->getSize();
+ if(j < 0)
+ j += m_inventory->getSize();
+
+ InventoryItem *item = m_inventory->getItem(j);
+ // Null items
+ if(item == NULL)
+ {
+ m_images[i]->setImage(NULL);
+
+ wchar_t t[10];
+ if(m_selection == j)
+ swprintf(t, 10, L"<-");
+ else
+ swprintf(t, 10, L"");
+ m_texts[i]->setText(t);
+
+ // The next ifs will segfault with a NULL pointer
+ continue;
+ }
+
+
+ m_images[i]->setImage(item->getImage());
+
+ wchar_t t[10];
+ if(m_selection == j)
+ swprintf(t, 10, SWPRINTF_CHARSTRING L" <-", item->getText().c_str());
+ else
+ swprintf(t, 10, SWPRINTF_CHARSTRING, item->getText().c_str());
+ m_texts[i]->setText(t);
+ }
+ }
+
+private:
+ s32 m_itemcount;
+ core::array<gui::IGUIStaticText*> m_texts;
+ core::array<gui::IGUIImage*> m_images;
+ Inventory *m_inventory;
+ s32 m_selection;
+};
+
+int main(int argc, char *argv[])
+{
+ /*
+ Low-level initialization
+ */
+
+ bool disable_stderr = false;
+#ifdef _WIN32
+ disable_stderr = true;
+#endif
+
+ debugstreams_init(disable_stderr, DEBUGFILE);
+ debug_stacks_init();
+
+
+ DSTACK(__FUNCTION_NAME);
+
+ try
+ {
+
+ /*
+ Basic initialization
+ */
+
+ dstream<<DTIME<<"minetest-c55"
+ " with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
+ <<", ENABLE_TESTS="<<ENABLE_TESTS
+ <<std::endl;
+
+ // Set locale. This is for forcing '.' as the decimal point.
+ std::locale::global(std::locale("C"));
+ // This enables printing all characters in bitmap font
+ setlocale(LC_CTYPE, "en_US");
+
+ // Initialize sockets
+ sockets_init();
+ atexit(sockets_cleanup);
+
+ // Initialize timestamp mutex
+ g_timestamp_mutex.Init();
+
+ /*
+ Run unit tests
+ */
+ if(ENABLE_TESTS)
+ {
+ run_tests();
+ }
+
+ /*
+ Initialization
+ */
+
+ // Read config file
+
+ if(argc >= 2)
+ {
+ readConfigFile(argv[1]);
+ }
+ else
+ {
+ const char *filenames[2] =
+ {
+ "../minetest.conf",
+ "../../minetest.conf"
+ };
+
+ for(u32 i=0; i<2; i++)
+ {
+ bool r = readConfigFile(filenames[i]);
+ if(r)
+ break;
+ }
+ }
+
+ // Initialize random seed
+ srand(time(0));
+
+ g_range_mutex.Init();
+ assert(g_range_mutex.IsInitialized());
+
+ /*
+ Ask some stuff
+ */
+
+ std::cout<<std::endl<<std::endl;
+ char templine[100];
+
+ std::cout<<"Dedicated server? [y = yes]: ";
+ if(g_dedicated_server != "")
+ {
+ std::cout<<g_dedicated_server<<std::endl;
+ snprintf(templine, 100, "%s", g_dedicated_server.c_str());
+ }
+ else
+ {
+ std::cin.getline(templine, 100);
+ }
+
+ bool dedicated = false;
+ if(templine[0] == 'y')
+ dedicated = true;
+ if(dedicated)
+ std::cout<<"-> yes"<<std::endl;
+ else
+ std::cout<<"-> no"<<std::endl;
+
+ std::cout<<"Port [empty=30000]: ";
+ if(g_port != "")
+ {
+ std::cout<<g_port<<std::endl;
+ snprintf(templine, 100, "%s", g_port.c_str());
+ }
+ else
+ {
+ std::cin.getline(templine, 100);
+ }
+ unsigned short port;
+ if(templine[0] == 0)
+ port = 30000;
+ else
+ port = atoi(templine);
+ std::cout<<"-> "<<port<<std::endl;
+
+ if(dedicated)
+ {
+ DSTACK("Dedicated server branch");
+
+ std::cout<<std::endl;
+ std::cout<<"========================"<<std::endl;
+ std::cout<<"Running dedicated server"<<std::endl;
+ std::cout<<"========================"<<std::endl;
+ std::cout<<std::endl;
+
+ Server server("../map", g_creative_mode, g_mapgen_params);
+ server.start(port);
+
+ for(;;)
+ {
+ // This is kind of a hack but can be done like this
+ // because server.step() is very light
+ sleep_ms(100);
+ server.step(0.1);
+
+ static int counter = 0;
+ counter--;
+ if(counter <= 0)
+ {
+ counter = 10;
+
+ core::list<PlayerInfo> list = server.getPlayerInfo();
+ core::list<PlayerInfo>::Iterator i;
+ static u32 sum_old = 0;
+ u32 sum = PIChecksum(list);
+ if(sum != sum_old)
+ {
+ std::cout<<DTIME<<"Player info:"<<std::endl;
+ for(i=list.begin(); i!=list.end(); i++)
+ {
+ i->PrintLine(&std::cout);
+ }
+ }
+ sum_old = sum;
+ }
+ }
+
+ return 0;
+ }
+
+ bool hosting = false;
+ char connect_name[100] = "";
+
+ std::cout<<"Address to connect to [empty = host a game]: ";
+ if(g_address != "" && is_yes(g_host_game) == false)
+ {
+ std::cout<<g_address<<std::endl;
+ snprintf(connect_name, 100, "%s", g_address.c_str());
+ }
+ else
+ {
+ std::cin.getline(connect_name, 100);
+ }
+
+ if(connect_name[0] == 0){
+ snprintf(connect_name, 100, "127.0.0.1");
+ hosting = true;
+ }
+
+ if(hosting)
+ std::cout<<"-> hosting"<<std::endl;
+ else
+ std::cout<<"-> "<<connect_name<<std::endl;
+
+ char playername[PLAYERNAME_SIZE] = "";
+ if(g_name != "")
+ {
+ snprintf(playername, PLAYERNAME_SIZE, "%s", g_name.c_str());
+ }
+ else
+ {
+ std::cout<<"Name of player: ";
+ std::cin.getline(playername, PLAYERNAME_SIZE);
+ }
+ std::cout<<"-> \""<<playername<<"\""<<std::endl;
+
+ /*
+ Resolution selection
+ */
+
+ u16 screenW;
+ u16 screenH;
+ bool fullscreen = false;
+
+ if(g_screenW != "" && g_screenH != "")
+ {
+ screenW = atoi(g_screenW.c_str());
+ screenH = atoi(g_screenH.c_str());
+ }
+ else
+ {
+ u16 resolutions[][3] = {
+ //W, H, fullscreen
+ {640,480, 0},
+ {800,600, 0},
+ {1024,768, 0},
+ {1280,1024, 0},
+ /*{640,480, 1},
+ {800,600, 1},
+ {1024,768, 1},
+ {1280,1024, 1},*/
+ };
+
+ u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
+
+ for(u16 i=0; i<res_count; i++)
+ {
+ std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
+ <<resolutions[i][1];
+ if(resolutions[i][2])
+ std::cout<<" fullscreen"<<std::endl;
+ else
+ std::cout<<" windowed"<<std::endl;
+ }
+ std::cout<<"Select a window resolution number [empty = 2]: ";
+ std::cin.getline(templine, 100);
+
+ u16 r0;
+ if(templine[0] == 0)
+ r0 = 2;
+ else
+ r0 = atoi(templine);
+
+ if(r0 > res_count || r0 == 0)
+ r0 = 2;
+
+ {
+ u16 i = r0-1;
+ std::cout<<"-> ";
+ std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
+ <<resolutions[i][1];
+ if(resolutions[i][2])
+ std::cout<<" fullscreen"<<std::endl;
+ else
+ std::cout<<" windowed"<<std::endl;
+ }
+
+ screenW = resolutions[r0-1][0];
+ screenH = resolutions[r0-1][1];
+ fullscreen = resolutions[r0-1][2];
+ }
+
+ //
+
+ MyEventReceiver receiver;
+
+ video::E_DRIVER_TYPE driverType;
+
+#ifdef _WIN32
+ //driverType = video::EDT_DIRECT3D9; // Doesn't seem to work
+ driverType = video::EDT_OPENGL;
+#else
+ driverType = video::EDT_OPENGL;
+#endif
+
+ // create device and exit if creation failed
+
+ IrrlichtDevice *device;
+ device = createDevice(driverType,
+ core::dimension2d<u32>(screenW, screenH),
+ 16, fullscreen, false, false, &receiver);
+ // With vsync
+ /*device = createDevice(driverType,
+ core::dimension2d<u32>(screenW, screenH),
+ 16, fullscreen, false, true, &receiver);*/
+
+ if (device == 0)
+ return 1; // could not create selected driver.
+
+ g_device = device;
+
+ device->setResizable(true);
+
+ if(g_random_input)
+ g_input = new RandomInputHandler();
+ else
+ g_input = new RealInputHandler(device, &receiver);
+
+ /*
+ Continue initialization
+ */
+
+ video::IVideoDriver* driver = device->getVideoDriver();
+ // These make the textures not to show at all
+ //driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
+ //driver->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_SPEED );
+
+ //driver->setMinHardwareBufferVertexCount(1);
+
+ scene::ISceneManager* smgr = device->getSceneManager();
+
+ gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
+ gui::IGUISkin* skin = guienv->getSkin();
+ gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
+ if(font)
+ skin->setFont(font);
+ //skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
+ skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
+ //skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
+ //skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(0,0,0,0));
+ skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255,0,0,0));
+ skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
+
+ const wchar_t *text = L"Loading and connecting...";
+ core::vector2d<s32> center(screenW/2, screenH/2);
+ core::dimension2d<u32> textd = font->getDimension(text);
+ std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
+ // Have to add a bit to disable the text from word wrapping
+ //core::vector2d<s32> textsize(textd.Width+4, textd.Height);
+ core::vector2d<s32> textsize(300, textd.Height);
+ core::rect<s32> textrect(center - textsize/2, center + textsize/2);
+
+ gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
+ text, textrect, false, false);
+ gui_loadingtext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+
+ driver->beginScene(true, true, video::SColor(255,0,0,0));
+ guienv->drawAll();
+ driver->endScene();
+
+ /*
+ Initialize material array
+ */
+
+ //video::SMaterial g_materials[MATERIALS_COUNT];
+ for(u16 i=0; i<MATERIALS_COUNT; i++)
+ {
+ g_materials[i].Lighting = false;
+ g_materials[i].BackfaceCulling = false;
+
+ const char *filename = g_material_filenames[i];
+ if(filename != NULL){
+ video::ITexture *t = driver->getTexture(filename);
+ if(t == NULL){
+ std::cout<<DTIME<<"Texture could not be loaded: \""
+ <<filename<<"\""<<std::endl;
+ return 1;
+ }
+ g_materials[i].setTexture(0, driver->getTexture(filename));
+ }
+ //g_materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
+ g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
+ //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
+ //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
+ if(i == MATERIAL_WATER)
+ {
+ g_materials[i].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ //g_materials[i].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
+ }
+ }
+
+ /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png"));
+ g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png"));
+ g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png"));
+ for(u32 i=0; i<3; i++)
+ {
+ g_mesh_materials[i].Lighting = false;
+ g_mesh_materials[i].BackfaceCulling = false;
+ g_mesh_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
+ g_mesh_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
+ }*/
+
+ // Make a scope here for the client so that it gets removed
+ // before the irrlicht device
+ {
+
+ std::cout<<DTIME<<"Creating server and client"<<std::endl;
+
+ /*
+ Create server
+ */
+ SharedPtr<Server> server;
+ if(hosting){
+ server = new Server("../map", g_creative_mode, g_mapgen_params);
+ server->start(port);
+ }
+
+ /*
+ Create client
+ */
+
+ // TODO: Get rid of the g_materials parameter or it's globalness
+ Client client(device, g_materials,
+ g_client_delete_unused_sectors_timeout,
+ playername);
+
+ Address connect_address(0,0,0,0, port);
+ try{
+ connect_address.Resolve(connect_name);
+ }
+ catch(ResolveError &e)
+ {
+ std::cout<<DTIME<<"Couldn't resolve address"<<std::endl;
+ return 0;
+ }
+
+ std::cout<<DTIME<<"Connecting to server..."<<std::endl;
+ client.connect(connect_address);
+
+ try{
+ while(client.connectedAndInitialized() == false)
+ {
+ client.step(0.1);
+ if(server != NULL){
+ server->step(0.1);
+ }
+ sleep_ms(100);
+ }
+ }
+ catch(con::PeerNotFoundException &e)
+ {
+ std::cout<<DTIME<<"Timed out."<<std::endl;
+ return 0;
+ }
+
+ /*
+ Create the camera node
+ */
+
+ scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(
+ 0, // Camera parent
+ v3f(BS*100, BS*2, BS*100), // Look from
+ v3f(BS*100+1, BS*2, BS*100), // Look to
+ -1 // Camera ID
+ );
+
+ if(camera == NULL)
+ return 1;
+
+ video::SColor skycolor = video::SColor(255,90,140,200);
+
+ camera->setFOV(FOV_ANGLE);
+
+ // Just so big a value that everything rendered is visible
+ camera->setFarValue(100000*BS);
+
+ /*//f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
+ f32 range = BS*HEIGHTMAP_RANGE_NODES*0.9;
+
+ camera->setFarValue(range);
+
+ driver->setFog(
+ skycolor,
+ video::EFT_FOG_LINEAR,
+ range*0.8,
+ range,
+ 0.01,
+ false,
+ false
+ );*/
+
+ f32 camera_yaw = 0; // "right/left"
+ f32 camera_pitch = 0; // "up/down"
+
+ gui_loadingtext->remove();
+
+ /*
+ Add some gui stuff
+ */
+
+ // First line of debug text
+ gui::IGUIStaticText *guitext = guienv->addStaticText(
+ L"Minetest-c55",
+ core::rect<s32>(5, 5, 5+600, 5+textsize.Y),
+ false, false);
+ // Second line of debug text
+ gui::IGUIStaticText *guitext2 = guienv->addStaticText(
+ L"",
+ core::rect<s32>(5, 5+(textsize.Y+5)*1, 5+600, (5+textsize.Y)*2),
+ false, false);
+
+ // At the middle of the screen
+ // Object infos are shown in this
+ gui::IGUIStaticText *guitext_info = guienv->addStaticText(
+ L"test",
+ core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
+ false, false);
+
+ // This is a copy of the inventory that the client's environment has
+ Inventory local_inventory(PLAYER_INVENTORY_SIZE);
+
+ GUIQuickInventory *quick_inventory = new GUIQuickInventory
+ (guienv, NULL, v2s32(10, 70), 5, &local_inventory);
+
+ /*
+ Some statistics are collected in these
+ */
+ u32 drawtime = 0;
+ u32 scenetime = 0;
+ u32 endscenetime = 0;
+
+ /*
+ Text input system
+ */
+
+ struct TextDest
+ {
+ virtual void sendText(std::string text) = 0;
+ };
+
+ struct TextDestSign : public TextDest
+ {
+ TextDestSign(v3s16 blockpos, s16 id, Client *client)
+ {
+ m_blockpos = blockpos;
+ m_id = id;
+ m_client = client;
+ }
+ void sendText(std::string text)
+ {
+ dstream<<"Changing text of a sign object: "
+ <<text<<std::endl;
+ m_client->sendSignText(m_blockpos, m_id, text);
+ }
+
+ v3s16 m_blockpos;
+ s16 m_id;
+ Client *m_client;
+ };
+
+ TextDest *textbuf_dest = NULL;
+
+ //gui::IGUIWindow* input_window = NULL;
+ gui::IGUIStaticText* input_guitext = NULL;
+
+ /*
+ Main loop
+ */
+
+ bool first_loop_after_window_activation = true;
+
+ // Time is in milliseconds
+ // NOTE: getRealTime() without run()s causes strange problems in wine
+ // NOTE: Have to call run() between calls of this to update the timer
+ u32 lasttime = device->getTimer()->getTime();
+
+ while(device->run())
+ {
+ // Hilight boxes collected during the loop and displayed
+ core::list< core::aabbox3d<f32> > hilightboxes;
+
+ // Info text
+ std::wstring infotext;
+
+ //TimeTaker //timer1("//timer1", device);
+
+ // Time of frame without fps limit
+ float busytime;
+ u32 busytime_u32;
+ {
+ // not using getRealTime is necessary for wine
+ u32 time = device->getTimer()->getTime();
+ if(time > lasttime)
+ busytime_u32 = time - lasttime;
+ else
+ busytime_u32 = 0;
+ busytime = busytime_u32 / 1000.0;
+ }
+
+ //std::cout<<"busytime_u32="<<busytime_u32<<std::endl;
+
+ // Absolutelu necessary for wine!
+ device->run();
+
+ /*
+ Viewing range
+ */
+
+ //updateViewingRange(dtime, &client);
+ updateViewingRange(busytime, &client);
+
+ /*
+ FPS limiter
+ */
+
+ {
+ float fps_max = g_fps_max;
+ u32 frametime_min = 1000./fps_max;
+
+ if(busytime_u32 < frametime_min)
+ {
+ u32 sleeptime = frametime_min - busytime_u32;
+ device->sleep(sleeptime);
+ }
+ }
+
+ // Absolutelu necessary for wine!
+ device->run();
+
+ /*
+ Time difference calculation
+ */
+ f32 dtime; // in seconds
+
+ u32 time = device->getTimer()->getTime();
+ if(time > lasttime)
+ dtime = (time - lasttime) / 1000.0;
+ else
+ dtime = 0;
+ lasttime = time;
+
+ /*
+ Time average and jitter calculation
+ */
+
+ static f32 dtime_avg1 = 0.0;
+ dtime_avg1 = dtime_avg1 * 0.98 + dtime * 0.02;
+ f32 dtime_jitter1 = dtime - dtime_avg1;
+
+ static f32 dtime_jitter1_max_sample = 0.0;
+ static f32 dtime_jitter1_max_fraction = 0.0;
+ {
+ static f32 jitter1_max = 0.0;
+ static f32 counter = 0.0;
+ if(dtime_jitter1 > jitter1_max)
+ jitter1_max = dtime_jitter1;
+ counter += dtime;
+ if(counter > 0.0)
+ {
+ counter -= 3.0;
+ dtime_jitter1_max_sample = jitter1_max;
+ dtime_jitter1_max_fraction
+ = dtime_jitter1_max_sample / (dtime_avg1+0.001);
+ jitter1_max = 0.0;
+
+ /*
+ Control freetime ratio
+ */
+ /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
+ {
+ if(g_freetime_ratio < FREETIME_RATIO_MAX)
+ g_freetime_ratio += 0.01;
+ }
+ else
+ {
+ if(g_freetime_ratio > FREETIME_RATIO_MIN)
+ g_freetime_ratio -= 0.01;
+ }*/
+ }
+ }
+
+ /*
+ Busytime average and jitter calculation
+ */
+
+ static f32 busytime_avg1 = 0.0;
+ busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
+ f32 busytime_jitter1 = busytime - busytime_avg1;
+
+ static f32 busytime_jitter1_max_sample = 0.0;
+ static f32 busytime_jitter1_min_sample = 0.0;
+ {
+ static f32 jitter1_max = 0.0;
+ static f32 jitter1_min = 0.0;
+ static f32 counter = 0.0;
+ if(busytime_jitter1 > jitter1_max)
+ jitter1_max = busytime_jitter1;
+ if(busytime_jitter1 < jitter1_min)
+ jitter1_min = busytime_jitter1;
+ counter += dtime;
+ if(counter > 0.0){
+ counter -= 3.0;
+ busytime_jitter1_max_sample = jitter1_max;
+ busytime_jitter1_min_sample = jitter1_min;
+ jitter1_max = 0.0;
+ jitter1_min = 0.0;
+ }
+ }
+
+ /*
+ Debug info for client
+ */
+ {
+ static float counter = 0.0;
+ counter -= dtime;
+ if(counter < 0)
+ {
+ counter = 30.0;
+ client.printDebugInfo(std::cout);
+ }
+ }
+
+ /*
+ Input handler step()
+ */
+ g_input->step(dtime);
+
+ /*
+ Special keys
+ */
+ if(g_esc_pressed)
+ {
+ break;
+ }
+
+ /*
+ Player speed control
+ */
+
+ if(g_game_focused)
+ {
+ /*bool a_up,
+ bool a_down,
+ bool a_left,
+ bool a_right,
+ bool a_jump,
+ bool a_superspeed,
+ float a_pitch,
+ float a_yaw*/
+ PlayerControl control(
+ g_input->isKeyDown(irr::KEY_KEY_W),
+ g_input->isKeyDown(irr::KEY_KEY_S),
+ g_input->isKeyDown(irr::KEY_KEY_A),
+ g_input->isKeyDown(irr::KEY_KEY_D),
+ g_input->isKeyDown(irr::KEY_SPACE),
+ g_input->isKeyDown(irr::KEY_KEY_2),
+ camera_pitch,
+ camera_yaw
+ );
+ client.setPlayerControl(control);
+ }
+ else
+ {
+ // Set every key to inactive
+ PlayerControl control;
+ client.setPlayerControl(control);
+ }
+
+ //timer1.stop();
+ /*
+ Process environment
+ */
+
+ {
+ //TimeTaker timer("client.step(dtime)", device);
+ client.step(dtime);
+ //client.step(dtime_avg1);
+ }
+
+ if(server != NULL)
+ {
+ //TimeTaker timer("server->step(dtime)", device);
+ server->step(dtime);
+ }
+
+ v3f player_position = client.getPlayerPosition();
+
+ //TimeTaker //timer2("//timer2", device);
+
+ /*
+ Mouse and camera control
+ */
+
+ if(device->isWindowActive() && g_game_focused)
+ {
+ device->getCursorControl()->setVisible(false);
+
+ if(first_loop_after_window_activation){
+ //std::cout<<"window active, first loop"<<std::endl;
+ first_loop_after_window_activation = false;
+ }
+ else{
+ s32 dx = g_input->getMousePos().X - 320;
+ s32 dy = g_input->getMousePos().Y - 240;
+ //std::cout<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
+ camera_yaw -= dx*0.2;
+ camera_pitch += dy*0.2;
+ if(camera_pitch < -89.5) camera_pitch = -89.5;
+ if(camera_pitch > 89.5) camera_pitch = 89.5;
+ }
+ g_input->setMousePos(320, 240);
+ }
+ else{
+ device->getCursorControl()->setVisible(true);
+
+ //std::cout<<"window inactive"<<std::endl;
+ first_loop_after_window_activation = true;
+ }
+
+ camera_yaw = wrapDegrees(camera_yaw);
+ camera_pitch = wrapDegrees(camera_pitch);
+
+ v3f camera_direction = v3f(0,0,1);
+ camera_direction.rotateYZBy(camera_pitch);
+ camera_direction.rotateXZBy(camera_yaw);
+
+ v3f camera_position =
+ player_position + v3f(0, BS+BS/2, 0);
+
+ camera->setPosition(camera_position);
+ // *100.0 helps in large map coordinates
+ camera->setTarget(camera_position + camera_direction * 100.0);
+
+ if(FIELD_OF_VIEW_TEST){
+ //client.m_env.getMap().updateCamera(v3f(0,0,0), v3f(0,0,1));
+ client.updateCamera(v3f(0,0,0), v3f(0,0,1));
+ }
+ else{
+ //client.m_env.getMap().updateCamera(camera_position, camera_direction);
+ //TimeTaker timer("client.updateCamera", device);
+ client.updateCamera(camera_position, camera_direction);
+ }
+
+ //timer2.stop();
+ //TimeTaker //timer3("//timer3", device);
+
+ /*
+ Calculate what block is the crosshair pointing to
+ */
+
+ //u32 t1 = device->getTimer()->getRealTime();
+
+ //f32 d = 4; // max. distance
+ f32 d = 4; // max. distance
+ core::line3d<f32> shootline(camera_position,
+ camera_position + camera_direction * BS * (d+1));
+
+ MapBlockObject *selected_object = client.getSelectedObject
+ (d*BS, camera_position, shootline);
+
+ if(selected_object != NULL)
+ {
+ //dstream<<"Client returned selected_object != NULL"<<std::endl;
+
+ core::aabbox3d<f32> box_on_map
+ = selected_object->getSelectionBoxOnMap();
+
+ hilightboxes.push_back(box_on_map);
+
+ infotext = narrow_to_wide(selected_object->infoText());
+
+ if(g_input->getLeftClicked())
+ {
+ std::cout<<DTIME<<"Left-clicked object"<<std::endl;
+ client.clickObject(0, selected_object->getBlock()->getPos(),
+ selected_object->getId(), g_selected_item);
+ }
+ else if(g_input->getRightClicked())
+ {
+ std::cout<<DTIME<<"Right-clicked object"<<std::endl;
+ /*
+ Check if we want to modify the object ourselves
+ */
+ if(selected_object->getTypeId() == MAPBLOCKOBJECT_TYPE_SIGN)
+ {
+ dstream<<"Sign object right-clicked"<<std::endl;
+
+ unFocusGame();
+
+ input_guitext = guienv->addStaticText(L"",
+ core::rect<s32>(150,100,350,120),
+ true, // border?
+ false, // wordwrap?
+ NULL);
+
+ input_guitext->setDrawBackground(true);
+
+ g_text_buffer = L"";
+ g_text_buffer_accepted = false;
+ textbuf_dest = new TextDestSign(
+ selected_object->getBlock()->getPos(),
+ selected_object->getId(),
+ &client);
+ }
+ /*
+ Otherwise pass the event to the server as-is
+ */
+ else
+ {
+ client.clickObject(1, selected_object->getBlock()->getPos(),
+ selected_object->getId(), g_selected_item);
+ }
+ }
+ }
+ else // selected_object == NULL
+ {
+
+ bool nodefound = false;
+ v3s16 nodepos;
+ v3s16 neighbourpos;
+ core::aabbox3d<f32> nodefacebox;
+ f32 mindistance = BS * 1001;
+
+ v3s16 pos_i = floatToInt(player_position);
+
+ /*std::cout<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
+ <<std::endl;*/
+
+ s16 a = d;
+ s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
+ s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
+ s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
+ s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
+ s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
+ s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
+
+ for(s16 y = ystart; y <= yend; y++){
+ for(s16 z = zstart; z <= zend; z++){
+ for(s16 x = xstart; x <= xend; x++)
+ {
+ try{
+ if(client.getNode(v3s16(x,y,z)).d == MATERIAL_AIR){
+ continue;
+ }
+ }catch(InvalidPositionException &e){
+ continue;
+ }
+
+ v3s16 np(x,y,z);
+ v3f npf = intToFloat(np);
+
+ f32 d = 0.01;
+
+ v3s16 directions[6] = {
+ v3s16(0,0,1), // back
+ v3s16(0,1,0), // top
+ v3s16(1,0,0), // right
+ v3s16(0,0,-1),
+ v3s16(0,-1,0),
+ v3s16(-1,0,0),
+ };
+
+ for(u16 i=0; i<6; i++){
+ //{u16 i=3;
+ v3f dir_f = v3f(directions[i].X,
+ directions[i].Y, directions[i].Z);
+ v3f centerpoint = npf + dir_f * BS/2;
+ f32 distance =
+ (centerpoint - camera_position).getLength();
+
+ if(distance < mindistance){
+ //std::cout<<DTIME<<"for centerpoint=("<<centerpoint.X<<","<<centerpoint.Y<<","<<centerpoint.Z<<"): distance < mindistance"<<std::endl;
+ //std::cout<<DTIME<<"npf=("<<npf.X<<","<<npf.Y<<","<<npf.Z<<")"<<std::endl;
+ core::CMatrix4<f32> m;
+ m.buildRotateFromTo(v3f(0,0,1), dir_f);
+
+ // This is the back face
+ v3f corners[2] = {
+ v3f(BS/2, BS/2, BS/2),
+ v3f(-BS/2, -BS/2, BS/2+d)
+ };
+
+ for(u16 j=0; j<2; j++){
+ m.rotateVect(corners[j]);
+ corners[j] += npf;
+ //std::cout<<DTIME<<"box corners["<<j<<"]: ("<<corners[j].X<<","<<corners[j].Y<<","<<corners[j].Z<<")"<<std::endl;
+ }
+
+ //core::aabbox3d<f32> facebox(corners[0],corners[1]);
+ core::aabbox3d<f32> facebox(corners[0]);
+ facebox.addInternalPoint(corners[1]);
+
+ if(facebox.intersectsWithLine(shootline)){
+ nodefound = true;
+ nodepos = np;
+ neighbourpos = np + directions[i];
+ mindistance = distance;
+ nodefacebox = facebox;
+ }
+ }
+ }
+ }}}
+
+ if(nodefound)
+ {
+ //std::cout<<DTIME<<"nodefound == true"<<std::endl;
+ //std::cout<<DTIME<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
+ //std::cout<<DTIME<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
+
+ static v3s16 nodepos_old(-1,-1,-1);
+ if(nodepos != nodepos_old){
+ std::cout<<DTIME<<"Pointing at ("<<nodepos.X<<","
+ <<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
+ nodepos_old = nodepos;
+
+ /*wchar_t positiontext[20];
+ swprintf(positiontext, 20, L"(%i,%i,%i)",
+ nodepos.X, nodepos.Y, nodepos.Z);
+ positiontextgui->setText(positiontext);*/
+ }
+
+ hilightboxes.push_back(nodefacebox);
+
+ if(g_input->getLeftClicked())
+ {
+ //std::cout<<DTIME<<"Removing node"<<std::endl;
+ //client.removeNode(nodepos);
+ std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
+ client.clickGround(0, nodepos, neighbourpos, g_selected_item);
+ }
+ if(g_input->getRightClicked())
+ {
+ //std::cout<<DTIME<<"Placing node"<<std::endl;
+ //client.addNodeFromInventory(neighbourpos, g_selected_item);
+ std::cout<<DTIME<<"Ground right-clicked"<<std::endl;
+ client.clickGround(1, nodepos, neighbourpos, g_selected_item);
+ }
+ }
+ else{
+ //std::cout<<DTIME<<"nodefound == false"<<std::endl;
+ //positiontextgui->setText(L"");
+ }
+
+ } // selected_object == NULL
+
+ g_input->resetLeftClicked();
+ g_input->resetRightClicked();
+
+ /*
+ Calculate stuff for drawing
+ */
+
+ v2u32 screensize = driver->getScreenSize();
+ core::vector2d<s32> displaycenter(screensize.X/2,screensize.Y/2);
+
+ camera->setAspectRatio((f32)screensize.X / (f32)screensize.Y);
+
+ /*
+ Update gui stuff (0ms)
+ */
+
+ //TimeTaker guiupdatetimer("Gui updating", device);
+
+ {
+ wchar_t temptext[100];
+
+ static float drawtime_avg = 0;
+ drawtime_avg = drawtime_avg * 0.98 + (float)drawtime*0.02;
+ static float scenetime_avg = 0;
+ scenetime_avg = scenetime_avg * 0.98 + (float)scenetime*0.02;
+ static float endscenetime_avg = 0;
+ endscenetime_avg = endscenetime_avg * 0.98 + (float)endscenetime*0.02;
+
+ swprintf(temptext, 100, L"Minetest-c55 ("
+ L"F: item=%i"
+ L", R: range_all=%i"
+ L")"
+ L" drawtime=%.0f, scenetime=%.0f, endscenetime=%.0f",
+ g_selected_item,
+ g_viewing_range_all,
+ drawtime_avg,
+ scenetime_avg,
+ endscenetime_avg
+ );
+
+ guitext->setText(temptext);
+ }
+
+ {
+ wchar_t temptext[100];
+ /*swprintf(temptext, 100,
+ L"("
+ L"% .3f < btime_jitter < % .3f"
+ L", dtime_jitter = % .1f %%"
+ //L", ftime_ratio = % .3f"
+ L")",
+ busytime_jitter1_min_sample,
+ busytime_jitter1_max_sample,
+ dtime_jitter1_max_fraction * 100.0
+ //g_freetime_ratio
+ );*/
+ swprintf(temptext, 100,
+ L"(% .1f, % .1f, % .1f)"
+ L" (% .3f < btime_jitter < % .3f"
+ L", dtime_jitter = % .1f %%)",
+ player_position.X/BS,
+ player_position.Y/BS,
+ player_position.Z/BS,
+ busytime_jitter1_min_sample,
+ busytime_jitter1_max_sample,
+ dtime_jitter1_max_fraction * 100.0
+ );
+
+ guitext2->setText(temptext);
+ }
+
+ {
+ /*wchar_t temptext[100];
+ swprintf(temptext, 100,
+ SWPRINTF_CHARSTRING,
+ infotext.substr(0,99).c_str()
+ );
+
+ guitext_info->setText(temptext);*/
+
+ guitext_info->setText(infotext.c_str());
+ }
+
+ /*
+ Inventory
+ */
+
+ static u16 old_selected_item = 65535;
+ if(client.getLocalInventoryUpdated()
+ || g_selected_item != old_selected_item)
+ {
+ old_selected_item = g_selected_item;
+ //std::cout<<"Updating local inventory"<<std::endl;
+ client.getLocalInventory(local_inventory);
+ quick_inventory->setSelection(g_selected_item);
+ quick_inventory->update();
+ }
+
+ if(input_guitext != NULL)
+ {
+ /*wchar_t temptext[100];
+ swprintf(temptext, 100,
+ SWPRINTF_CHARSTRING,
+ g_text_buffer.substr(0,99).c_str()
+ );*/
+ input_guitext->setText(g_text_buffer.c_str());
+ }
+
+ /*
+ Text input stuff
+ */
+ if(input_guitext != NULL && g_text_buffer_accepted)
+ {
+ input_guitext->remove();
+ input_guitext = NULL;
+
+ if(textbuf_dest != NULL)
+ {
+ std::string text = wide_to_narrow(g_text_buffer);
+ dstream<<"Sending text: "<<text<<std::endl;
+ textbuf_dest->sendText(text);
+ delete textbuf_dest;
+ textbuf_dest = NULL;
+ }
+
+ focusGame();
+ }
+
+ //guiupdatetimer.stop();
+
+ /*
+ Drawing begins
+ */
+
+ TimeTaker drawtimer("Drawing", device);
+
+ /*
+ Background color is choosen based on whether the player is
+ much beyond the initial ground level
+ */
+ /*video::SColor bgcolor;
+ v3s16 p0 = Map::floatToInt(player_position);
+ // Does this make short random delays?
+ // NOTE: no need for this, sky doesn't show underground with
+ // enough range
+ bool is_underground = client.isNodeUnderground(p0);
+ //bool is_underground = false;
+ if(is_underground == false)
+ bgcolor = video::SColor(255,90,140,200);
+ else
+ bgcolor = video::SColor(255,0,0,0);*/
+
+ //video::SColor bgcolor = video::SColor(255,90,140,200);
+ video::SColor bgcolor = skycolor;
+
+ // 0ms
+ driver->beginScene(true, true, bgcolor);
+
+ //timer3.stop();
+
+ //std::cout<<DTIME<<"smgr->drawAll()"<<std::endl;
+
+ {
+ TimeTaker timer("smgr", device);
+ smgr->drawAll();
+ scenetime = timer.stop(true);
+ }
+
+ {
+ //TimeTaker timer9("auxiliary drawings", device);
+ // 0ms
+
+ driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0),
+ displaycenter + core::vector2d<s32>(10,0),
+ video::SColor(255,255,255,255));
+ driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10),
+ displaycenter + core::vector2d<s32>(0,10),
+ video::SColor(255,255,255,255));
+
+ //timer9.stop();
+ //TimeTaker //timer10("//timer10", device);
+
+ video::SMaterial m;
+ m.Thickness = 10;
+ m.Lighting = false;
+ driver->setMaterial(m);
+
+ driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
+
+ for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
+ i != hilightboxes.end(); i++)
+ {
+ /*std::cout<<"hilightbox min="
+ <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")"
+ <<" max="
+ <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")"
+ <<std::endl;*/
+ driver->draw3DBox(*i, video::SColor(255,0,0,0));
+ }
+
+ }
+
+ //timer10.stop();
+ //TimeTaker //timer11("//timer11", device);
+
+ /*
+ Draw gui
+ */
+ // 0-1ms
+ guienv->drawAll();
+
+ // End drawing
+ {
+ TimeTaker timer("endScene", device);
+ driver->endScene();
+ endscenetime = timer.stop(true);
+ }
+
+ drawtime = drawtimer.stop(true);
+
+ /*
+ Drawing ends
+ */
+
+ static s16 lastFPS = 0;
+ //u16 fps = driver->getFPS();
+ u16 fps = (1.0/dtime_avg1);
+
+ if (lastFPS != fps)
+ {
+ core::stringw str = L"Minetest [";
+ str += driver->getName();
+ str += "] FPS:";
+ str += fps;
+
+ device->setWindowCaption(str.c_str());
+ lastFPS = fps;
+ }
+
+ /*}
+ else
+ device->yield();*/
+ }
+
+ } // client is deleted at this point
+
+ delete g_input;
+
+ /*
+ In the end, delete the Irrlicht device.
+ */
+ device->drop();
+
+ } //try
+ catch(con::PeerNotFoundException &e)
+ {
+ dstream<<DTIME<<"Connection timed out."<<std::endl;
+ }
+#if CATCH_EXCEPTIONS
+ /*
+ This is what has to be done in every thread to get suitable debug info
+ */
+ catch(std::exception &e)
+ {
+ dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
+ <<e.what()<<std::endl;
+ assert(0);
+ }
+#endif
+
+ debugstreams_deinit();
+
+ return 0;
+}
+
+//END