979 lines
24 KiB
C++
979 lines
24 KiB
C++
/*
|
|
(c) 2010 Perttu Ahola <celeron55@gmail.com>
|
|
|
|
Minetest
|
|
|
|
TODO: Check for obsolete todos
|
|
TODO: Storing map on disk, preferably dynamically
|
|
TODO: struct settings, with a mutex and get/set functions
|
|
TODO: Implement torches and similar light sources (halfway done)
|
|
TODO: A menu
|
|
TODO: A cache class that can be used with lightNeighbors,
|
|
unlightNeighbors and probably many others. Look for
|
|
implementation in lightNeighbors
|
|
TODO: Proper objects for random stuff in this file, like
|
|
g_selected_material
|
|
TODO: See if changing to 32-bit position variables doesn't raise
|
|
memory consumption a lot.
|
|
Now:
|
|
TODO: Have to implement mutexes to MapSectors; otherwise initial
|
|
lighting might fail
|
|
TODO: Adding more heightmap points to MapSectors
|
|
|
|
Network protocol:
|
|
- Client map data is only updated from the server's,
|
|
EXCEPT FOR lighting.
|
|
|
|
Actions:
|
|
|
|
- User places block
|
|
-> Client sends PLACED_BLOCK(pos, node)
|
|
-> Server validates and sends MAP_SINGLE_CHANGE(pos, node)
|
|
-> Client applies change and recalculates lighting and face cache
|
|
|
|
- User starts digging
|
|
-> Client sends START_DIGGING(pos)
|
|
-> Server starts timer
|
|
- if user stops digging:
|
|
-> Client sends STOP_DIGGING
|
|
-> Server stops timer
|
|
- if user continues:
|
|
-> Server waits timer
|
|
-> Server sends MAP_SINGLE_CHANGE(pos, node)
|
|
-> Client applies change and recalculates lighting and face cache
|
|
|
|
*/
|
|
|
|
/*
|
|
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
|
|
|
|
// Enable unit tests
|
|
#define ENABLE_TESTS 0
|
|
|
|
#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 <time.h>
|
|
#include <jthread/jmutexautolock.h>
|
|
namespace jthread {} // JThread 1.2 support
|
|
using namespace jthread; // JThread 1.3 support
|
|
#include "common_irrlicht.h"
|
|
#include "map.h"
|
|
#include "player.h"
|
|
#include "main.h"
|
|
#include "test.h"
|
|
#include "environment.h"
|
|
#include "server.h"
|
|
#include "client.h"
|
|
#include <string>
|
|
|
|
const char *g_material_filenames[MATERIALS_COUNT] =
|
|
{
|
|
"../data/stone.png",
|
|
"../data/grass.png",
|
|
"../data/water.png",
|
|
};
|
|
|
|
#define FPS_MIN 15
|
|
#define FPS_MAX 25
|
|
|
|
#define VIEWING_RANGE_NODES_MIN MAP_BLOCKSIZE
|
|
#define VIEWING_RANGE_NODES_MAX 35
|
|
|
|
JMutex g_viewing_range_nodes_mutex;
|
|
s16 g_viewing_range_nodes = MAP_BLOCKSIZE;
|
|
|
|
/*
|
|
Random stuff
|
|
*/
|
|
u16 g_selected_material = 0;
|
|
|
|
/*
|
|
Debug streams
|
|
- use these to disable or enable outputs of parts of the program
|
|
*/
|
|
|
|
std::ofstream dfile("debug.txt");
|
|
//std::ofstream dfile2("debug2.txt");
|
|
|
|
// Connection
|
|
//std::ostream dout_con(std::cout.rdbuf());
|
|
std::ostream dout_con(dfile.rdbuf());
|
|
|
|
// Server
|
|
//std::ostream dout_server(std::cout.rdbuf());
|
|
std::ostream dout_server(dfile.rdbuf());
|
|
|
|
// Client
|
|
//std::ostream dout_client(std::cout.rdbuf());
|
|
std::ostream dout_client(dfile.rdbuf());
|
|
|
|
/*
|
|
TimeTaker
|
|
*/
|
|
|
|
class TimeTaker
|
|
{
|
|
public:
|
|
TimeTaker(const char *name, IrrlichtDevice *dev)
|
|
{
|
|
m_name = name;
|
|
m_dev = dev;
|
|
m_time1 = m_dev->getTimer()->getRealTime();
|
|
}
|
|
~TimeTaker()
|
|
{
|
|
u32 time2 = m_dev->getTimer()->getRealTime();
|
|
u32 dtime = time2 - m_time1;
|
|
std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
|
|
}
|
|
private:
|
|
const char *m_name;
|
|
IrrlichtDevice *m_dev;
|
|
u32 m_time1;
|
|
};
|
|
|
|
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){
|
|
if(event.KeyInput.Key == irr::KEY_KEY_F){
|
|
if(g_selected_material < USEFUL_MATERIAL_COUNT-1)
|
|
g_selected_material++;
|
|
else
|
|
g_selected_material = 0;
|
|
std::cout<<"Selected material: "
|
|
<<g_selected_material<<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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
void updateViewingRange(f32 frametime)
|
|
{
|
|
#if 1
|
|
static f32 counter = 0;
|
|
if(counter > 0){
|
|
counter -= frametime;
|
|
return;
|
|
}
|
|
counter = 5.0; //seconds
|
|
|
|
g_viewing_range_nodes_mutex.Lock();
|
|
bool changed = false;
|
|
if(frametime > 1.0/FPS_MIN
|
|
|| g_viewing_range_nodes > VIEWING_RANGE_NODES_MAX){
|
|
if(g_viewing_range_nodes > VIEWING_RANGE_NODES_MIN){
|
|
g_viewing_range_nodes -= MAP_BLOCKSIZE/2;
|
|
changed = true;
|
|
}
|
|
}
|
|
else if(frametime < 1.0/FPS_MAX
|
|
|| g_viewing_range_nodes < VIEWING_RANGE_NODES_MIN){
|
|
if(g_viewing_range_nodes < VIEWING_RANGE_NODES_MAX){
|
|
g_viewing_range_nodes += MAP_BLOCKSIZE/2;
|
|
changed = true;
|
|
}
|
|
}
|
|
if(changed){
|
|
std::cout<<"g_viewing_range_nodes = "
|
|
<<g_viewing_range_nodes<<std::endl;
|
|
}
|
|
g_viewing_range_nodes_mutex.Unlock();
|
|
#endif
|
|
}
|
|
|
|
s16 temp16;
|
|
f32 tempf;
|
|
v3f tempv3f1;
|
|
v3f tempv3f2;
|
|
|
|
void SpeedTests(IrrlichtDevice *device)
|
|
{
|
|
/*
|
|
Test stuff
|
|
*/
|
|
|
|
//test();
|
|
//return 0;
|
|
/*TestThread thread;
|
|
thread.Start();
|
|
std::cout<<"thread started"<<std::endl;
|
|
while(thread.IsRunning()) sleep(1);
|
|
std::cout<<"thread ended"<<std::endl;
|
|
return 0;*/
|
|
|
|
{
|
|
std::cout<<"Testing floating-point conversion speed"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
tempf = 0.001;
|
|
for(u32 i=0; i<10000000; i++){
|
|
temp16 += tempf;
|
|
tempf += 0.001;
|
|
}
|
|
u32 time2 = device->getTimer()->getRealTime();
|
|
u32 fp_conversion_time = time2 - time1;
|
|
std::cout<<"Done. "<<fp_conversion_time<<"ms"<<std::endl;
|
|
//assert(fp_conversion_time < 1000);
|
|
}
|
|
|
|
{
|
|
std::cout<<"Testing floating-point vector speed"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
|
|
tempv3f1 = v3f(1,2,3);
|
|
tempv3f2 = v3f(4,5,6);
|
|
for(u32 i=0; i<40000000; i++){
|
|
tempf += tempv3f1.dotProduct(tempv3f2);
|
|
tempv3f2 += v3f(7,8,9);
|
|
}
|
|
|
|
u32 time2 = device->getTimer()->getRealTime();
|
|
u32 dtime = time2 - time1;
|
|
std::cout<<"Done. "<<dtime<<"ms"<<std::endl;
|
|
}
|
|
|
|
{
|
|
std::cout<<"Testing core::map speed"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
|
|
core::map<v2s16, f32> map1;
|
|
tempf = -324;
|
|
for(s16 y=0; y<500; y++){
|
|
for(s16 x=0; x<500; x++){
|
|
map1.insert(v2s16(x,y), tempf);
|
|
tempf += 1;
|
|
}
|
|
}
|
|
for(s16 y=500-1; y>=0; y--){
|
|
for(s16 x=0; x<500; x++){
|
|
tempf = map1[v2s16(x,y)];
|
|
}
|
|
}
|
|
|
|
u32 time2 = device->getTimer()->getRealTime();
|
|
u32 dtime = time2 - time1;
|
|
std::cout<<"Done. "<<dtime<<"ms"<<std::endl;
|
|
}
|
|
|
|
{
|
|
std::cout<<"Testing mutex speed"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
u32 time2 = time1;
|
|
|
|
JMutex m;
|
|
m.Init();
|
|
u32 n = 0;
|
|
u32 i = 0;
|
|
do{
|
|
n += 10000;
|
|
for(; i<n; i++){
|
|
m.Lock();
|
|
m.Unlock();
|
|
}
|
|
time2 = device->getTimer()->getRealTime();
|
|
}
|
|
// Do at least 10ms
|
|
while(time2 < time1 + 10);
|
|
|
|
u32 dtime = time2 - time1;
|
|
u32 per_ms = n / dtime;
|
|
std::cout<<"Done. "<<dtime<<"ms, "
|
|
<<per_ms<<"/ms"<<std::endl;
|
|
}
|
|
|
|
//assert(0);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
sockets_init();
|
|
atexit(sockets_cleanup);
|
|
|
|
/*
|
|
Run unit tests
|
|
*/
|
|
if(ENABLE_TESTS)
|
|
run_tests();
|
|
|
|
//return 0; //DEBUG
|
|
|
|
/*
|
|
Initialization
|
|
*/
|
|
|
|
srand(time(0));
|
|
|
|
g_viewing_range_nodes_mutex.Init();
|
|
assert(g_viewing_range_nodes_mutex.IsInitialized());
|
|
|
|
MyEventReceiver receiver;
|
|
|
|
// create device and exit if creation failed
|
|
|
|
/*
|
|
Host selection
|
|
*/
|
|
char connect_name[100];
|
|
std::cout<<std::endl<<std::endl;
|
|
std::cout<<"Address to connect to [empty = host a game]: ";
|
|
std::cin.getline(connect_name, 100);
|
|
|
|
bool hosting = false;
|
|
if(connect_name[0] == 0){
|
|
snprintf(connect_name, 100, "127.0.0.1");
|
|
hosting = true;
|
|
}
|
|
std::cout<<"-> "<<connect_name<<std::endl;
|
|
|
|
std::cout<<"Port [empty=30000]: ";
|
|
char templine[100];
|
|
std::cin.getline(templine, 100);
|
|
unsigned short port;
|
|
if(templine[0] == 0)
|
|
port = 30000;
|
|
else
|
|
port = atoi(templine);
|
|
|
|
/*
|
|
Resolution selection
|
|
*/
|
|
|
|
u16 screenW = 800;
|
|
u16 screenH = 600;
|
|
|
|
/*
|
|
u16 resolutions[][2] = {
|
|
{640,480},
|
|
{800,600},
|
|
{1024,768},
|
|
{1280,1024}
|
|
};
|
|
|
|
u16 res_count = sizeof(resolutions)/sizeof(resolutions[0]);
|
|
|
|
std::cout<<"Select window resolution "
|
|
<<"(type a number and press enter):"<<std::endl;
|
|
for(u16 i=0; i<res_count; i++)
|
|
{
|
|
std::cout<<(i+1)<<": "<<resolutions[i][0]<<"x"
|
|
<<resolutions[i][1]<<std::endl;
|
|
}
|
|
u16 r0;
|
|
std::cin>>r0;
|
|
if(r0 > res_count || r0 == 0)
|
|
r0 = 0;
|
|
u16 screenW = resolutions[r0-1][0];
|
|
u16 screenH = resolutions[r0-1][1];
|
|
*/
|
|
|
|
//
|
|
|
|
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
|
|
|
|
IrrlichtDevice *device;
|
|
device = createDevice(driverType,
|
|
core::dimension2d<u32>(screenW, screenH),
|
|
16, false, false, false, &receiver);
|
|
|
|
if (device == 0)
|
|
return 1; // could not create selected driver.
|
|
|
|
/*
|
|
Run some speed tests
|
|
*/
|
|
//SpeedTests(device);
|
|
|
|
/*
|
|
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 );
|
|
|
|
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...";
|
|
core::vector2d<s32> center(screenW/2, screenH/2);
|
|
core::dimension2d<u32> textd = font->getDimension(text);
|
|
std::cout<<"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();
|
|
|
|
video::SMaterial materials[MATERIALS_COUNT];
|
|
for(u16 i=0; i<MATERIALS_COUNT; i++)
|
|
{
|
|
materials[i].Lighting = false;
|
|
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<<"Texture could not be loaded: \""
|
|
<<filename<<"\""<<std::endl;
|
|
return 1;
|
|
}
|
|
materials[i].setTexture(0, driver->getTexture(filename));
|
|
}
|
|
//materials[i].setFlag(video::EMF_TEXTURE_WRAP, video::ETC_REPEAT);
|
|
materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
|
|
//materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
|
|
}
|
|
|
|
// Make a scope here for the client so that it gets removed
|
|
// before the irrlicht device
|
|
{
|
|
|
|
std::cout<<"Creating server and client"<<std::endl;
|
|
|
|
Server *server = NULL;
|
|
if(hosting){
|
|
server = new Server();
|
|
server->start(port);
|
|
}
|
|
|
|
Client client(smgr, materials);
|
|
Address connect_address(0,0,0,0, port);
|
|
try{
|
|
connect_address.Resolve(connect_name);
|
|
}
|
|
catch(ResolveError &e)
|
|
{
|
|
std::cout<<"Couldn't resolve address"<<std::endl;
|
|
return 0;
|
|
}
|
|
client.connect(connect_address);
|
|
|
|
Player *player = client.getLocalPlayer();
|
|
|
|
/*
|
|
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;
|
|
|
|
camera->setFOV(FOV_ANGLE);
|
|
// Just so big a value that everything rendered is visible
|
|
camera->setFarValue(BS*1000);
|
|
|
|
f32 camera_yaw = 0; // "right/left"
|
|
f32 camera_pitch = 0; // "up/down"
|
|
|
|
// Random constants
|
|
#define WALK_ACCELERATION (4.0 * BS)
|
|
#define WALKSPEED_MAX (4.0 * BS)
|
|
//#define WALKSPEED_MAX (20.0 * BS)
|
|
f32 walk_acceleration = WALK_ACCELERATION;
|
|
f32 walkspeed_max = WALKSPEED_MAX;
|
|
|
|
/*
|
|
The mouse cursor needs not be visible, so we hide it via the
|
|
irr::IrrlichtDevice::ICursorControl.
|
|
*/
|
|
device->getCursorControl()->setVisible(false);
|
|
|
|
gui_loadingtext->remove();
|
|
|
|
gui::IGUIStaticText *guitext = guienv->addStaticText(
|
|
L"Minetest-c55", core::rect<s32>(5, 5, 5+300, 5+textsize.Y),
|
|
false, false);
|
|
/*
|
|
Main loop
|
|
*/
|
|
|
|
bool first_loop_after_window_activation = true;
|
|
|
|
s32 lastFPS = -1;
|
|
|
|
// Time is in milliseconds
|
|
u32 lasttime = device->getTimer()->getTime();
|
|
|
|
while(device->run())
|
|
{
|
|
/*
|
|
Time difference calculation
|
|
*/
|
|
u32 time = device->getTimer()->getTime();
|
|
f32 dtime; // in seconds
|
|
if(time > lasttime)
|
|
dtime = (time - lasttime) / 1000.0;
|
|
else
|
|
dtime = 0;
|
|
lasttime = time;
|
|
|
|
updateViewingRange(dtime);
|
|
|
|
// Collected during the loop and displayed
|
|
core::list< core::aabbox3d<f32> > hilightboxes;
|
|
|
|
/*
|
|
Special keys
|
|
*/
|
|
if(receiver.IsKeyDown(irr::KEY_ESCAPE))
|
|
{
|
|
break;
|
|
}
|
|
|
|
/*
|
|
Player speed control
|
|
*/
|
|
|
|
v3f move_direction = v3f(0,0,1);
|
|
move_direction.rotateXZBy(camera_yaw);
|
|
|
|
v3f speed = v3f(0,0,0);
|
|
if(receiver.IsKeyDown(irr::KEY_KEY_W))
|
|
{
|
|
speed += move_direction;
|
|
}
|
|
if(receiver.IsKeyDown(irr::KEY_KEY_S))
|
|
{
|
|
speed -= move_direction;
|
|
}
|
|
if(receiver.IsKeyDown(irr::KEY_KEY_A))
|
|
{
|
|
speed += move_direction.crossProduct(v3f(0,1,0));
|
|
}
|
|
if(receiver.IsKeyDown(irr::KEY_KEY_D))
|
|
{
|
|
speed += move_direction.crossProduct(v3f(0,-1,0));
|
|
}
|
|
if(receiver.IsKeyDown(irr::KEY_SPACE))
|
|
{
|
|
if(player->touching_ground){
|
|
//player_speed.Y = 30*BS;
|
|
//player.speed.Y = 5*BS;
|
|
player->speed.Y = 6.5*BS;
|
|
}
|
|
}
|
|
|
|
// The speed of the player (Y is ignored)
|
|
speed = speed.normalize() * walkspeed_max;
|
|
|
|
f32 inc = walk_acceleration * BS * dtime;
|
|
|
|
if(player->speed.X < speed.X - inc)
|
|
player->speed.X += inc;
|
|
else if(player->speed.X > speed.X + inc)
|
|
player->speed.X -= inc;
|
|
else if(player->speed.X < speed.X)
|
|
player->speed.X = speed.X;
|
|
else if(player->speed.X > speed.X)
|
|
player->speed.X = speed.X;
|
|
|
|
if(player->speed.Z < speed.Z - inc)
|
|
player->speed.Z += inc;
|
|
else if(player->speed.Z > speed.Z + inc)
|
|
player->speed.Z -= inc;
|
|
else if(player->speed.Z < speed.Z)
|
|
player->speed.Z = speed.Z;
|
|
else if(player->speed.Z > speed.Z)
|
|
player->speed.Z = speed.Z;
|
|
|
|
/*
|
|
Process environment
|
|
*/
|
|
|
|
{
|
|
//TimeTaker("client.step(dtime)", device);
|
|
client.step(dtime);
|
|
}
|
|
|
|
if(server != NULL){
|
|
//TimeTaker("server->step(dtime)", device);
|
|
server->step(dtime);
|
|
}
|
|
|
|
/*
|
|
Mouse and camera control
|
|
*/
|
|
|
|
if(device->isWindowActive())
|
|
{
|
|
if(first_loop_after_window_activation){
|
|
first_loop_after_window_activation = false;
|
|
}
|
|
else{
|
|
s32 dx = device->getCursorControl()->getPosition().X - 320;
|
|
s32 dy = device->getCursorControl()->getPosition().Y - 240;
|
|
camera_yaw -= dx*0.2;
|
|
camera_pitch += dy*0.2;
|
|
if(camera_pitch < -89.9) camera_pitch = -89.9;
|
|
if(camera_pitch > 89.9) camera_pitch = 89.9;
|
|
}
|
|
device->getCursorControl()->setPosition(320, 240);
|
|
}
|
|
else{
|
|
first_loop_after_window_activation = true;
|
|
}
|
|
|
|
v3f camera_direction = v3f(0,0,1);
|
|
camera_direction.rotateYZBy(camera_pitch);
|
|
camera_direction.rotateXZBy(camera_yaw);
|
|
|
|
v3f camera_position =
|
|
player->getPosition() + v3f(0, BS+BS/2, 0);
|
|
|
|
camera->setPosition(camera_position);
|
|
camera->setTarget(camera_position + camera_direction);
|
|
|
|
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);
|
|
client.updateCamera(camera_position, camera_direction);
|
|
}
|
|
|
|
/*
|
|
Calculate what block is the crosshair pointing to
|
|
*/
|
|
|
|
//u32 t1 = device->getTimer()->getTime();
|
|
|
|
f32 d = 4; // max. distance
|
|
core::line3d<f32> shootline(camera_position,
|
|
camera_position + camera_direction * BS * (d+1));
|
|
|
|
bool nodefound = false;
|
|
v3s16 nodepos;
|
|
v3s16 neighbourpos;
|
|
core::aabbox3d<f32> nodefacebox;
|
|
f32 mindistance = BS * 1001;
|
|
|
|
v3s16 pos_i = Map::floatToInt(player->getPosition());
|
|
|
|
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.m_env.getMap().getNode(x,y,z).d == MATERIAL_AIR){
|
|
if(client.getNode(v3s16(x,y,z)).d == MATERIAL_AIR){
|
|
continue;
|
|
}
|
|
}catch(InvalidPositionException &e){
|
|
continue;
|
|
}
|
|
|
|
v3s16 np(x,y,z);
|
|
v3f npf = Map::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<<"for centerpoint=("<<centerpoint.X<<","<<centerpoint.Y<<","<<centerpoint.Z<<"): distance < mindistance"<<std::endl;
|
|
//std::cout<<"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<<"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<<"nodefound == true"<<std::endl;
|
|
//std::cout<<"nodepos=("<<nodepos.X<<","<<nodepos.Y<<","<<nodepos.Z<<")"<<std::endl;
|
|
//std::cout<<"neighbourpos=("<<neighbourpos.X<<","<<neighbourpos.Y<<","<<neighbourpos.Z<<")"<<std::endl;
|
|
|
|
static v3s16 nodepos_old(-1,-1,-1);
|
|
if(nodepos != nodepos_old){
|
|
std::cout<<"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(receiver.leftclicked){
|
|
std::cout<<"Removing block (MapNode)"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
|
|
//client.m_env.getMap().removeNodeAndUpdate(nodepos);
|
|
client.removeNode(nodepos);
|
|
|
|
u32 time2 = device->getTimer()->getRealTime();
|
|
u32 dtime = time2 - time1;
|
|
std::cout<<"Took "<<dtime<<"ms"<<std::endl;
|
|
}
|
|
if(receiver.rightclicked){
|
|
std::cout<<"Placing block (MapNode)"<<std::endl;
|
|
u32 time1 = device->getTimer()->getRealTime();
|
|
|
|
/*f32 light = client.m_env.getMap().getNode(neighbourpos).light;
|
|
MapNode n;
|
|
n.d = g_selected_material;
|
|
client.m_env.getMap().setNode(neighbourpos, n);
|
|
client.m_env.getMap().nodeAddedUpdate(neighbourpos, light);*/
|
|
MapNode n;
|
|
n.d = g_selected_material;
|
|
client.addNode(neighbourpos, n);
|
|
|
|
u32 time2 = device->getTimer()->getRealTime();
|
|
u32 dtime = time2 - time1;
|
|
std::cout<<"Took "<<dtime<<"ms"<<std::endl;
|
|
}
|
|
}
|
|
else{
|
|
//std::cout<<"nodefound == false"<<std::endl;
|
|
//positiontextgui->setText(L"");
|
|
}
|
|
|
|
receiver.leftclicked = false;
|
|
receiver.rightclicked = false;
|
|
|
|
/*
|
|
Update gui stuff
|
|
*/
|
|
|
|
static u8 old_selected_material = MATERIAL_AIR;
|
|
if(g_selected_material != old_selected_material)
|
|
{
|
|
old_selected_material = g_selected_material;
|
|
wchar_t temptext[50];
|
|
swprintf(temptext, 50, L"Minetest-c55 (F: material=%i)",
|
|
g_selected_material);
|
|
guitext->setText(temptext);
|
|
}
|
|
|
|
/*
|
|
Drawing begins
|
|
*/
|
|
|
|
/*
|
|
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);
|
|
s16 gy = client.m_env.getMap().getGroundHeight(v2s16(p0.X, p0.Z));
|
|
if(p0.Y > gy - MAP_BLOCKSIZE)
|
|
bgcolor = video::SColor(255,90,140,200);
|
|
else
|
|
bgcolor = video::SColor(255,0,0,0);*/
|
|
video::SColor bgcolor = video::SColor(255,90,140,200);
|
|
|
|
driver->beginScene(true, true, bgcolor);
|
|
|
|
//std::cout<<"smgr->drawAll()"<<std::endl;
|
|
|
|
smgr->drawAll();
|
|
|
|
core::vector2d<s32> displaycenter(screenW/2,screenH/2);
|
|
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));
|
|
|
|
video::SMaterial m;
|
|
m.Thickness = 10;
|
|
m.Lighting = false;
|
|
driver->setMaterial(m);
|
|
|
|
for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin();
|
|
i != hilightboxes.end(); i++){
|
|
driver->draw3DBox(*i, video::SColor(255,0,0,0));
|
|
}
|
|
|
|
guienv->drawAll();
|
|
|
|
driver->endScene();
|
|
|
|
/*
|
|
Drawing ends
|
|
*/
|
|
|
|
u16 fps = driver->getFPS();
|
|
|
|
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();*/
|
|
}
|
|
|
|
if(server != NULL)
|
|
delete server;
|
|
|
|
} // client is deleted at this point
|
|
|
|
/*
|
|
In the end, delete the Irrlicht device.
|
|
*/
|
|
device->drop();
|
|
|
|
return 0;
|
|
}
|
|
|
|
//END
|