mirror of
https://github.com/moparisthebest/minetest
synced 2024-11-07 18:05:08 -05:00
2340 lines
57 KiB
C++
2340 lines
57 KiB
C++
|
/*
|
||
|
(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
|