forked from oerkki/voxelands
1288 lines
28 KiB
C++
1288 lines
28 KiB
C++
/************************************************************************
|
|
* Minetest-c55
|
|
* Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
|
|
*
|
|
* main.cpp
|
|
* voxelands - 3d voxel world sandbox game
|
|
* Copyright (C) Lisa 'darkrose' Milne 2013-2014 <lisa@ltmnet.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
* License updated from GPLv2 or later to GPLv3 or later by Lisa Milne
|
|
* for Voxelands.
|
|
************************************************************************/
|
|
|
|
#include "common.h"
|
|
|
|
#ifdef NDEBUG
|
|
// 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")
|
|
#pragma comment(lib, "zlibwapi.lib")
|
|
#pragma comment(lib, "Shell32.lib")
|
|
// This would get rid of the console window
|
|
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
|
|
#endif
|
|
|
|
#include "irrlicht.h" // createDevice
|
|
|
|
#include "main.h"
|
|
#include "mainmenumanager.h"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <locale.h>
|
|
#include "common_irrlicht.h"
|
|
#include "debug.h"
|
|
#include "test.h"
|
|
#include "server.h"
|
|
#include "constants.h"
|
|
#include "porting.h"
|
|
#include "gettime.h"
|
|
#include "guiMessageMenu.h"
|
|
#include "config.h"
|
|
#include "guiMainMenu.h"
|
|
#include "mineral.h"
|
|
#include "game.h"
|
|
#include "keycode.h"
|
|
#include "tile.h"
|
|
#include "intl.h"
|
|
#include "profiler.h"
|
|
#include "log.h"
|
|
// for the init functions
|
|
#include "content_craft.h"
|
|
#include "content_clothesitem.h"
|
|
#include "content_craftitem.h"
|
|
#include "content_toolitem.h"
|
|
#include "content_mob.h"
|
|
#include "path.h"
|
|
#include "gui_colours.h"
|
|
#include "character_creator.h"
|
|
#include "thread.h"
|
|
#if USE_FREETYPE
|
|
#include "xCGUITTFont.h"
|
|
#endif
|
|
#include "sound.h"
|
|
|
|
// This makes textures
|
|
ITextureSource *g_texturesource = NULL;
|
|
|
|
// Global profiler
|
|
Profiler main_profiler;
|
|
Profiler *g_profiler = &main_profiler;
|
|
|
|
/*
|
|
Random stuff
|
|
*/
|
|
|
|
/*
|
|
mainmenumanager.h
|
|
*/
|
|
|
|
gui::IGUIEnvironment* guienv = NULL;
|
|
gui::IGUIStaticText *guiroot = NULL;
|
|
MainMenuManager g_menumgr;
|
|
|
|
bool noMenuActive()
|
|
{
|
|
return (g_menumgr.menuCount() == 0);
|
|
}
|
|
|
|
// Passed to menus to allow disconnecting and exiting
|
|
MainGameCallback *g_gamecallback = NULL;
|
|
|
|
/*
|
|
Debug streams
|
|
*/
|
|
|
|
// Connection
|
|
std::ostream *dout_con_ptr = &dummyout;
|
|
std::ostream *derr_con_ptr = &verbosestream;
|
|
//std::ostream *dout_con_ptr = &infostream;
|
|
//std::ostream *derr_con_ptr = &errorstream;
|
|
|
|
// Server
|
|
std::ostream *dout_server_ptr = &infostream;
|
|
std::ostream *derr_server_ptr = &errorstream;
|
|
|
|
// Client
|
|
std::ostream *dout_client_ptr = &infostream;
|
|
std::ostream *derr_client_ptr = &errorstream;
|
|
|
|
/*
|
|
gettime.h implementation
|
|
*/
|
|
|
|
// A small helper class
|
|
class TimeGetter
|
|
{
|
|
public:
|
|
virtual u32 getTime() = 0;
|
|
};
|
|
|
|
// A precise irrlicht one
|
|
class IrrlichtTimeGetter: public TimeGetter
|
|
{
|
|
public:
|
|
IrrlichtTimeGetter(IrrlichtDevice *device):
|
|
m_device(device)
|
|
{}
|
|
u32 getTime()
|
|
{
|
|
if(m_device == NULL)
|
|
return 0;
|
|
return m_device->getTimer()->getRealTime();
|
|
}
|
|
private:
|
|
IrrlichtDevice *m_device;
|
|
};
|
|
// Not so precise one which works without irrlicht
|
|
class SimpleTimeGetter: public TimeGetter
|
|
{
|
|
public:
|
|
u32 getTime()
|
|
{
|
|
return porting::getTimeMs();
|
|
}
|
|
};
|
|
|
|
// A pointer to a global instance of the time getter
|
|
// TODO: why?
|
|
TimeGetter *g_timegetter = NULL;
|
|
|
|
u32 getTimeMs()
|
|
{
|
|
if(g_timegetter == NULL)
|
|
return 0;
|
|
return g_timegetter->getTime();
|
|
}
|
|
|
|
/*
|
|
Event handler for Irrlicht
|
|
|
|
NOTE: Everything possible should be moved out from here,
|
|
probably to InputHandler and the_game
|
|
*/
|
|
|
|
class MyEventReceiver : public IEventReceiver
|
|
{
|
|
public:
|
|
// This is the one method that we have to implement
|
|
virtual bool OnEvent(const SEvent& event)
|
|
{
|
|
/*
|
|
React to nothing here if a menu is active
|
|
*/
|
|
if(noMenuActive() == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Remember whether each key is down or up
|
|
if(event.EventType == irr::EET_KEY_INPUT_EVENT)
|
|
{
|
|
if(event.KeyInput.PressedDown) {
|
|
keyIsDown.set(event.KeyInput);
|
|
keyWasDown.set(event.KeyInput);
|
|
} else {
|
|
keyIsDown.unset(event.KeyInput);
|
|
}
|
|
}
|
|
|
|
if(event.EventType == irr::EET_MOUSE_INPUT_EVENT)
|
|
{
|
|
if(noMenuActive() == false)
|
|
{
|
|
left_active = false;
|
|
middle_active = false;
|
|
right_active = false;
|
|
}
|
|
else
|
|
{
|
|
left_active = event.MouseInput.isLeftPressed();
|
|
middle_active = event.MouseInput.isMiddlePressed();
|
|
right_active = event.MouseInput.isRightPressed();
|
|
|
|
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_LMOUSE_LEFT_UP)
|
|
{
|
|
leftreleased = true;
|
|
}
|
|
if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
|
|
{
|
|
rightreleased = true;
|
|
}
|
|
if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
|
|
{
|
|
mouse_wheel += event.MouseInput.Wheel;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool IsKeyDown(const KeyPress &keyCode) const
|
|
{
|
|
return keyIsDown[keyCode];
|
|
}
|
|
|
|
// Checks whether a key was down and resets the state
|
|
bool WasKeyDown(const KeyPress &keyCode)
|
|
{
|
|
bool b = keyWasDown[keyCode];
|
|
if (b)
|
|
keyWasDown.unset(keyCode);
|
|
return b;
|
|
}
|
|
|
|
s32 getMouseWheel()
|
|
{
|
|
s32 a = mouse_wheel;
|
|
mouse_wheel = 0;
|
|
return a;
|
|
}
|
|
|
|
void clearInput()
|
|
{
|
|
keyIsDown.clear();
|
|
keyWasDown.clear();
|
|
|
|
leftclicked = false;
|
|
rightclicked = false;
|
|
leftreleased = false;
|
|
rightreleased = false;
|
|
|
|
left_active = false;
|
|
middle_active = false;
|
|
right_active = false;
|
|
|
|
mouse_wheel = 0;
|
|
}
|
|
|
|
MyEventReceiver()
|
|
{
|
|
clearInput();
|
|
}
|
|
|
|
bool leftclicked;
|
|
bool rightclicked;
|
|
bool leftreleased;
|
|
bool rightreleased;
|
|
|
|
bool left_active;
|
|
bool middle_active;
|
|
bool right_active;
|
|
|
|
s32 mouse_wheel;
|
|
|
|
private:
|
|
// The current state of keys
|
|
KeyList keyIsDown;
|
|
// Whether a key has been pressed or not
|
|
KeyList keyWasDown;
|
|
};
|
|
|
|
/*
|
|
Separated input handler
|
|
*/
|
|
|
|
class RealInputHandler : public InputHandler
|
|
{
|
|
public:
|
|
RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
|
|
m_device(device),
|
|
m_receiver(receiver)
|
|
{
|
|
}
|
|
virtual bool isKeyDown(const KeyPress &keyCode)
|
|
{
|
|
return m_receiver->IsKeyDown(keyCode);
|
|
}
|
|
virtual bool wasKeyDown(const KeyPress &keyCode)
|
|
{
|
|
return m_receiver->WasKeyDown(keyCode);
|
|
}
|
|
virtual v2s32 getMousePos()
|
|
{
|
|
return m_device->getCursorControl()->getPosition();
|
|
}
|
|
virtual void setMousePos(s32 x, s32 y)
|
|
{
|
|
m_device->getCursorControl()->setPosition(x, y);
|
|
}
|
|
|
|
virtual bool getLeftState()
|
|
{
|
|
return m_receiver->left_active;
|
|
}
|
|
virtual bool getRightState()
|
|
{
|
|
return m_receiver->right_active;
|
|
}
|
|
|
|
virtual bool getLeftClicked()
|
|
{
|
|
return m_receiver->leftclicked;
|
|
}
|
|
virtual bool getRightClicked()
|
|
{
|
|
return m_receiver->rightclicked;
|
|
}
|
|
virtual void resetLeftClicked()
|
|
{
|
|
m_receiver->leftclicked = false;
|
|
}
|
|
virtual void resetRightClicked()
|
|
{
|
|
m_receiver->rightclicked = false;
|
|
}
|
|
|
|
virtual bool getLeftReleased()
|
|
{
|
|
return m_receiver->leftreleased;
|
|
}
|
|
virtual bool getRightReleased()
|
|
{
|
|
return m_receiver->rightreleased;
|
|
}
|
|
virtual void resetLeftReleased()
|
|
{
|
|
m_receiver->leftreleased = false;
|
|
}
|
|
virtual void resetRightReleased()
|
|
{
|
|
m_receiver->rightreleased = false;
|
|
}
|
|
|
|
virtual s32 getMouseWheel()
|
|
{
|
|
return m_receiver->getMouseWheel();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
m_receiver->clearInput();
|
|
}
|
|
private:
|
|
IrrlichtDevice *m_device;
|
|
MyEventReceiver *m_receiver;
|
|
};
|
|
|
|
class RandomInputHandler : public InputHandler
|
|
{
|
|
public:
|
|
RandomInputHandler()
|
|
{
|
|
leftdown = false;
|
|
rightdown = false;
|
|
leftclicked = false;
|
|
rightclicked = false;
|
|
leftreleased = false;
|
|
rightreleased = false;
|
|
keydown.clear();
|
|
}
|
|
virtual bool isKeyDown(const KeyPress &keyCode)
|
|
{
|
|
return keydown[keyCode];
|
|
}
|
|
virtual bool wasKeyDown(const KeyPress &keyCode)
|
|
{
|
|
return false;
|
|
}
|
|
virtual v2s32 getMousePos()
|
|
{
|
|
return mousepos;
|
|
}
|
|
virtual void setMousePos(s32 x, s32 y)
|
|
{
|
|
mousepos = v2s32(x,y);
|
|
}
|
|
|
|
virtual bool getLeftState()
|
|
{
|
|
return leftdown;
|
|
}
|
|
virtual bool getRightState()
|
|
{
|
|
return rightdown;
|
|
}
|
|
|
|
virtual bool getLeftClicked()
|
|
{
|
|
return leftclicked;
|
|
}
|
|
virtual bool getRightClicked()
|
|
{
|
|
return rightclicked;
|
|
}
|
|
virtual void resetLeftClicked()
|
|
{
|
|
leftclicked = false;
|
|
}
|
|
virtual void resetRightClicked()
|
|
{
|
|
rightclicked = false;
|
|
}
|
|
|
|
virtual bool getLeftReleased()
|
|
{
|
|
return leftreleased;
|
|
}
|
|
virtual bool getRightReleased()
|
|
{
|
|
return rightreleased;
|
|
}
|
|
virtual void resetLeftReleased()
|
|
{
|
|
leftreleased = false;
|
|
}
|
|
virtual void resetRightReleased()
|
|
{
|
|
rightreleased = false;
|
|
}
|
|
|
|
virtual s32 getMouseWheel()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
virtual void step(float dtime)
|
|
{
|
|
{
|
|
static float counter1 = 0;
|
|
counter1 -= dtime;
|
|
if(counter1 < 0.0)
|
|
{
|
|
counter1 = 0.1*Rand(1, 40);
|
|
keydown.toggle(getKeySetting(VLKC_JUMP));
|
|
}
|
|
}
|
|
{
|
|
static float counter1 = 0;
|
|
counter1 -= dtime;
|
|
if(counter1 < 0.0)
|
|
{
|
|
counter1 = 0.1*Rand(1, 40);
|
|
keydown.toggle(getKeySetting(VLKC_USE));
|
|
}
|
|
}
|
|
{
|
|
static float counter1 = 0;
|
|
counter1 -= dtime;
|
|
if(counter1 < 0.0)
|
|
{
|
|
counter1 = 0.1*Rand(1, 40);
|
|
keydown.toggle(getKeySetting(VLKC_FORWARD));
|
|
}
|
|
}
|
|
{
|
|
static float counter1 = 0;
|
|
counter1 -= dtime;
|
|
if(counter1 < 0.0)
|
|
{
|
|
counter1 = 0.1*Rand(1, 40);
|
|
keydown.toggle(getKeySetting(VLKC_LEFT));
|
|
}
|
|
}
|
|
{
|
|
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);
|
|
leftdown = !leftdown;
|
|
if(leftdown)
|
|
leftclicked = true;
|
|
if(!leftdown)
|
|
leftreleased = true;
|
|
}
|
|
}
|
|
{
|
|
static float counter1 = 0;
|
|
counter1 -= dtime;
|
|
if(counter1 < 0.0)
|
|
{
|
|
counter1 = 0.1*Rand(1, 15);
|
|
rightdown = !rightdown;
|
|
if(rightdown)
|
|
rightclicked = true;
|
|
if(!rightdown)
|
|
rightreleased = true;
|
|
}
|
|
}
|
|
mousepos += mousespeed;
|
|
}
|
|
|
|
s32 Rand(s32 min, s32 max)
|
|
{
|
|
return (myrand()%(max-min+1))+min;
|
|
}
|
|
private:
|
|
KeyList keydown;
|
|
v2s32 mousepos;
|
|
v2s32 mousespeed;
|
|
bool leftdown;
|
|
bool rightdown;
|
|
bool leftclicked;
|
|
bool rightclicked;
|
|
bool leftreleased;
|
|
bool rightreleased;
|
|
};
|
|
|
|
// These are defined global so that they're not optimized too much.
|
|
// Can't change them to volatile.
|
|
s16 temp16;
|
|
f32 tempf;
|
|
v3f tempv3f1;
|
|
v3f tempv3f2;
|
|
std::string tempstring;
|
|
std::string tempstring2;
|
|
|
|
void SpeedTests()
|
|
{
|
|
{
|
|
dstream<<"The following test should take around 20ms."<<std::endl;
|
|
TimeTaker timer("Testing std::string speed");
|
|
const u32 jj = 10000;
|
|
for(u32 j=0; j<jj; j++)
|
|
{
|
|
tempstring = "";
|
|
tempstring2 = "";
|
|
const u32 ii = 10;
|
|
for(u32 i=0; i<ii; i++){
|
|
tempstring2 += "asd";
|
|
}
|
|
for(u32 i=0; i<ii+1; i++){
|
|
tempstring += "asd";
|
|
if(tempstring == tempstring2)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
dstream<<"All of the following tests should take around 100ms each."
|
|
<<std::endl;
|
|
|
|
{
|
|
TimeTaker timer("Testing floating-point conversion speed");
|
|
tempf = 0.001;
|
|
for(u32 i=0; i<4000000; i++){
|
|
temp16 += tempf;
|
|
tempf += 0.001;
|
|
}
|
|
}
|
|
|
|
{
|
|
TimeTaker timer("Testing floating-point vector speed");
|
|
|
|
tempv3f1 = v3f(1,2,3);
|
|
tempv3f2 = v3f(4,5,6);
|
|
for(u32 i=0; i<10000000; i++){
|
|
tempf += tempv3f1.dotProduct(tempv3f2);
|
|
tempv3f2 += v3f(7,8,9);
|
|
}
|
|
}
|
|
|
|
{
|
|
TimeTaker timer("Testing core::map speed");
|
|
|
|
core::map<v2s16, f32> map1;
|
|
tempf = -324;
|
|
const s16 ii=300;
|
|
for(s16 y=0; y<ii; y++){
|
|
for(s16 x=0; x<ii; x++){
|
|
map1.insert(v2s16(x,y), tempf);
|
|
tempf += 1;
|
|
}
|
|
}
|
|
for(s16 y=ii-1; y>=0; y--){
|
|
for(s16 x=0; x<ii; x++){
|
|
tempf = map1[v2s16(x,y)];
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
dstream<<"Around 5000/ms should do well here."<<std::endl;
|
|
TimeTaker timer("Testing mutex speed");
|
|
|
|
JMutex m;
|
|
m.Init();
|
|
u32 n = 0;
|
|
u32 i = 0;
|
|
do{
|
|
n += 10000;
|
|
for(; i<n; i++){
|
|
m.Lock();
|
|
m.Unlock();
|
|
}
|
|
}
|
|
// Do at least 10ms
|
|
while(timer.getTime() < 10);
|
|
|
|
u32 dtime = timer.stop();
|
|
u32 per_ms = n / dtime;
|
|
dstream<<"Done. "<<dtime<<"ms, "
|
|
<<per_ms<<"/ms"<<std::endl;
|
|
}
|
|
}
|
|
|
|
void drawMenuBackground(video::IVideoDriver* driver)
|
|
{
|
|
char buff[1024];
|
|
core::dimension2d<s32> screensize(driver->getScreenSize());
|
|
|
|
video::ITexture *background = NULL;
|
|
if (path_get((char*)"texture",(char*)"menubg0.png",1,buff,1024))
|
|
background = driver->getTexture(buff);
|
|
|
|
if (!background)
|
|
return;
|
|
|
|
core::dimension2d<s32> texturesize(background->getSize());
|
|
|
|
core::rect<s32> image(0,0,texturesize.Width,texturesize.Height);
|
|
core::rect<s32> pos(
|
|
(texturesize.Width/2)-(screensize.Width/2),
|
|
(texturesize.Height/2)-(screensize.Height/2),
|
|
texturesize.Width,
|
|
texturesize.Height
|
|
);
|
|
driver->draw2DImage(
|
|
background,
|
|
image,
|
|
pos,
|
|
NULL,
|
|
NULL,
|
|
true
|
|
);
|
|
}
|
|
|
|
class StderrLogOutput: public ILogOutput
|
|
{
|
|
public:
|
|
/* line: Full line with timestamp, level and thread */
|
|
void printLog(const std::string &line)
|
|
{
|
|
std::cerr<<line<<std::endl;
|
|
}
|
|
} main_stderr_log_out;
|
|
|
|
class DstreamNoStderrLogOutput: public ILogOutput
|
|
{
|
|
public:
|
|
/* line: Full line with timestamp, level and thread */
|
|
void printLog(const std::string &line)
|
|
{
|
|
dstream_no_stderr<<line<<std::endl;
|
|
}
|
|
} main_dstream_no_stderr_log_out;
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
/*
|
|
Initialization
|
|
*/
|
|
|
|
log_add_output_maxlev(&main_stderr_log_out, LMT_ACTION);
|
|
log_add_output_all_levs(&main_dstream_no_stderr_log_out);
|
|
|
|
log_register_thread("main");
|
|
|
|
/*
|
|
Low-level initialization
|
|
*/
|
|
|
|
bool disable_stderr = false;
|
|
|
|
porting::signal_handler_init();
|
|
bool &kill = *porting::signal_handler_killstatus();
|
|
|
|
thread_init();
|
|
path_init();
|
|
intl_init();
|
|
command_init();
|
|
config_init(argc,argv);
|
|
|
|
// Initialize debug streams
|
|
{
|
|
char buff[1024];
|
|
if (!path_get(NULL,(char*)"debug.txt",0,buff,1024))
|
|
return 1;
|
|
debugstreams_init(disable_stderr, buff);
|
|
}
|
|
// Initialize debug stacks
|
|
debug_stacks_init();
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
// Init material properties table
|
|
//initializeMaterialProperties();
|
|
|
|
// Debug handler
|
|
BEGIN_DEBUG_EXCEPTION_HANDLER
|
|
|
|
// Print startup message
|
|
actionstream<<PROJECT_NAME<<
|
|
" with SER_FMT_VER_HIGHEST="<<(int)SER_FMT_VER_HIGHEST
|
|
<<", "<<BUILD_INFO
|
|
<<std::endl;
|
|
|
|
// Initialize sockets
|
|
sockets_init();
|
|
atexit(sockets_cleanup);
|
|
|
|
// Initialize random seed
|
|
srand(time(0));
|
|
mysrand(time(0));
|
|
|
|
/*
|
|
Pre-initialize some stuff with a dummy irrlicht wrapper.
|
|
|
|
These are needed for unit tests at least.
|
|
*/
|
|
|
|
crafting::initCrafting();
|
|
content_clothesitem_init();
|
|
content_craftitem_init();
|
|
content_toolitem_init();
|
|
content_mob_init();
|
|
// Initial call with g_texturesource not set.
|
|
init_mapnode(NULL);
|
|
// Must be called before g_texturesource is created
|
|
// (for texture atlas making)
|
|
init_mineral();
|
|
|
|
/*
|
|
Game parameters
|
|
*/
|
|
|
|
// Run dedicated server if asked to
|
|
if (config_get_bool("server")) {
|
|
DSTACK("Dedicated server branch");
|
|
|
|
// Create time getter
|
|
g_timegetter = new SimpleTimeGetter();
|
|
|
|
char* v = config_get("server.world");
|
|
world_init(v);
|
|
|
|
// Create server
|
|
Server server;
|
|
server.start();
|
|
|
|
// Run server
|
|
dedicated_server_loop(server, kill);
|
|
|
|
world_exit();
|
|
|
|
config_save(NULL,NULL,NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Device initialization
|
|
*/
|
|
|
|
// Resolution selection
|
|
|
|
bool fullscreen = config_get_bool("client.video.fullscreen");
|
|
u16 screenW = config_get_int("client.video.size.width");
|
|
u16 screenH = config_get_int("client.video.size.height");
|
|
|
|
// bpp, fsaa, vsync
|
|
|
|
bool vsync = config_get_bool("client.video.vsync");
|
|
u16 bits = config_get_int("client.video.fullscreen.bpp");
|
|
u16 fsaa = config_get_int("client.video.fullscreen.fsaa");
|
|
|
|
// Determine driver
|
|
|
|
video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
|
|
|
|
{
|
|
char* v = config_get("client.video.driver");
|
|
if (v && !strcmp(v,"d3d9"))
|
|
driverType = video::EDT_DIRECT3D9;
|
|
}
|
|
|
|
/*
|
|
Create device and exit if creation failed
|
|
*/
|
|
|
|
MyEventReceiver receiver;
|
|
|
|
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
|
params.DriverType = driverType;
|
|
params.WindowSize = core::dimension2d<u32>(screenW, screenH);
|
|
params.Bits = bits;
|
|
params.AntiAlias = fsaa;
|
|
params.Fullscreen = fullscreen;
|
|
params.Stencilbuffer = false;
|
|
params.Vsync = vsync;
|
|
params.EventReceiver = &receiver;
|
|
params.HighPrecisionFPU = config_get_bool("client.video.hpfpu");
|
|
|
|
IrrlichtDevice * device = createDeviceEx(params);
|
|
|
|
if (device == 0)
|
|
return 1; // could not create selected driver.
|
|
|
|
/*
|
|
Continue initialization
|
|
*/
|
|
|
|
video::IVideoDriver* driver = device->getVideoDriver();
|
|
|
|
// Disable mipmaps (because some of them look ugly)
|
|
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
|
|
|
|
// Set the window caption
|
|
device->setWindowCaption(L"Voxelands");
|
|
drawLoadingScreen(device,L"");
|
|
|
|
// Create time getter
|
|
g_timegetter = new IrrlichtTimeGetter(device);
|
|
|
|
// Create game callback for menus
|
|
g_gamecallback = new MainGameCallback(device);
|
|
|
|
// Create texture source
|
|
g_texturesource = new TextureSource(device);
|
|
|
|
drawLoadingScreen(device,L"");
|
|
|
|
device->setResizable(true);
|
|
|
|
InputHandler *input = new RealInputHandler(device, &receiver);
|
|
|
|
scene::ISceneManager* smgr = device->getSceneManager();
|
|
|
|
guienv = device->getGUIEnvironment();
|
|
gui::IGUISkin* skin = guienv->getSkin();
|
|
gui::IGUIFont* font = NULL;
|
|
{
|
|
char buff[1024];
|
|
#if USE_FREETYPE
|
|
uint16_t font_size = config_get_int("client.ui.font.size");
|
|
char* v = config_get("client.ui.font");
|
|
if (!v)
|
|
v = "arimo.ttf";
|
|
if (path_get("font",v,1,buff,1024))
|
|
font = gui::CGUITTFont::createTTFont(guienv, buff, font_size, true, true, 1, 128);
|
|
#else
|
|
if (path_get("texture","fontlucida.png",1,buff,1024))
|
|
font = guienv->getFont(buff);
|
|
#endif
|
|
}
|
|
if (font) {
|
|
skin->setFont(font);
|
|
}else{
|
|
errorstream<<"WARNING: Font file was not found. Using default font."<<std::endl;
|
|
}
|
|
// If font was not found, this will get us one
|
|
font = skin->getFont();
|
|
assert(font);
|
|
drawLoadingScreen(device,narrow_to_wide(gettext("Setting Up UI")));
|
|
|
|
u32 text_height = font->getDimension(L"Hello, world!").Height;
|
|
infostream<<"text_height="<<text_height<<std::endl;
|
|
|
|
skin->setColor(gui::EGDC_3D_HIGH_LIGHT, GUI_3D_HIGH_LIGHT);
|
|
skin->setColor(gui::EGDC_3D_SHADOW, GUI_3D_SHADOW);
|
|
skin->setColor(gui::EGDC_HIGH_LIGHT, GUI_HIGH_LIGHT);
|
|
skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, GUI_HIGH_LIGHT_TEXT);
|
|
skin->setColor(gui::EGDC_BUTTON_TEXT, GUI_BUTTON_TEXT);
|
|
|
|
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
|
|
// Irrlicht 1.8 input colours
|
|
skin->setColor(gui::EGDC_3D_FACE, GUI_3D_FACE);
|
|
skin->setColor(gui::EGDC_3D_DARK_SHADOW, GUI_3D_DARK_SHADOW);
|
|
skin->setColor(gui::EGDC_EDITABLE, GUI_EDITABLE);
|
|
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, GUI_FOCUSED_EDITABLE);
|
|
#endif
|
|
|
|
/*
|
|
Preload some textures and stuff
|
|
*/
|
|
|
|
drawLoadingScreen(device,narrow_to_wide(gettext("Loading MapNodes")));
|
|
init_mapnode(device); // Second call with g_texturesource set
|
|
drawLoadingScreen(device,narrow_to_wide(gettext("Loading Creatures")));
|
|
content_mob_init();
|
|
// preloading this reduces some hud flicker
|
|
g_texturesource->getTextureId("crack.png");
|
|
|
|
drawLoadingScreen(device,narrow_to_wide(gettext("Setting Up Sound")));
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_init();
|
|
#endif
|
|
|
|
/*
|
|
GUI stuff
|
|
*/
|
|
|
|
/*
|
|
If an error occurs, this is set to something and the
|
|
menu-game loop is restarted. It is then displayed before
|
|
the menu.
|
|
*/
|
|
std::wstring error_message = L"";
|
|
|
|
// The password entered during the menu screen,
|
|
std::string password;
|
|
|
|
// if there's no chardef then put the player directly into the character creator
|
|
bool character_creator = true;
|
|
{
|
|
char* v = config_get("client.character");
|
|
if (v && v[0])
|
|
character_creator = false;
|
|
}
|
|
|
|
/*
|
|
Menu-game loop
|
|
*/
|
|
while (device->run() && kill == false) {
|
|
|
|
// This is used for catching disconnects
|
|
try{
|
|
|
|
/*
|
|
Clear everything from the GUIEnvironment
|
|
*/
|
|
guienv->clear();
|
|
|
|
/*
|
|
We need some kind of a root node to be able to add
|
|
custom gui elements directly on the screen.
|
|
Otherwise they won't be automatically drawn.
|
|
*/
|
|
guiroot = guienv->addStaticText(L"",core::rect<s32>(0, 0, 10000, 10000));
|
|
|
|
/*
|
|
Out-of-game menu loop.
|
|
Loop quits when menu returns proper parameters.
|
|
break out immediately if we should be in the character creator
|
|
*/
|
|
while (kill == false && character_creator == false) {
|
|
// Cursor can be non-visible when coming from the game
|
|
device->getCursorControl()->setVisible(true);
|
|
// Some stuff are left to scene manager when coming from the game
|
|
// (map at least?)
|
|
smgr->clear();
|
|
|
|
// Initialize menu data
|
|
std::string playername = "";
|
|
{
|
|
char* v = config_get("client.name");
|
|
if (v)
|
|
playername = std::string(v);
|
|
}
|
|
if (playername == "")
|
|
playername = porting::getUser();
|
|
MainMenuData menudata;
|
|
menudata.address = L"";
|
|
menudata.name = narrow_to_wide(playername);
|
|
menudata.port = L"";
|
|
menudata.game_mode = narrow_to_wide(bridge_config_get("world.game.mode"));
|
|
menudata.max_mob_level = narrow_to_wide(bridge_config_get("world.game.mob.spawn.level"));
|
|
menudata.initial_inventory = config_get_bool("world.player.inventory.starter");
|
|
menudata.infinite_inventory = config_get_bool("world.player.inventory.creative");
|
|
menudata.droppable_inventory = config_get_bool("world.player.inventory.droppable");
|
|
menudata.death_drops_inventory = config_get_bool("world.player.inventory.keep");
|
|
menudata.enable_damage = config_get_bool("world.player.damage");
|
|
menudata.suffocation = config_get_bool("world.player.suffocation");
|
|
menudata.hunger = config_get_bool("world.player.hunger");
|
|
menudata.tool_wear = config_get_bool("world.player.tool.wear");
|
|
menudata.unsafe_fire = config_get_bool("world.game.environment.fire.spread");
|
|
menudata.delete_map = false;
|
|
menudata.clear_map = false;
|
|
menudata.use_fixed_seed = false;
|
|
menudata.fixed_seed = narrow_to_wide(bridge_config_get("world.seed"));
|
|
if (menudata.fixed_seed != L"")
|
|
menudata.use_fixed_seed = true;
|
|
menudata.map_type = config_get("world.map.type");
|
|
|
|
GUIMainMenu *menu = new GUIMainMenu(
|
|
guienv,
|
|
guiroot,
|
|
-1,
|
|
&g_menumgr,
|
|
&menudata,
|
|
g_gamecallback
|
|
);
|
|
menu->allowFocusRemoval(true);
|
|
|
|
if (error_message != L"") {
|
|
errorstream<<"error_message = "
|
|
<<wide_to_narrow(error_message)<<std::endl;
|
|
|
|
GUIMessageMenu *menu2 =
|
|
new GUIMessageMenu(guienv, guiroot, -1,
|
|
&g_menumgr, error_message.c_str());
|
|
menu2->drop();
|
|
error_message = L"";
|
|
}
|
|
|
|
video::IVideoDriver* driver = device->getVideoDriver();
|
|
|
|
infostream<<"Created main menu"<<std::endl;
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_play_music("bg-mainmenu",1.0,1);
|
|
#endif
|
|
|
|
while (device->run() && kill == false) {
|
|
if (menu->getStatus() == true)
|
|
break;
|
|
if (g_gamecallback->startgame_requested) {
|
|
g_gamecallback->startgame_requested = false;
|
|
menu->quitMenu();
|
|
break;
|
|
}
|
|
|
|
driver->beginScene(true, true, video::SColor(255,170,230,255));
|
|
|
|
drawMenuBackground(driver);
|
|
|
|
guienv->drawAll();
|
|
|
|
driver->endScene();
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_step(0.02,NULL,NULL,NULL);
|
|
#endif
|
|
|
|
// On some computers framerate doesn't seem to be
|
|
// automatically limited
|
|
sleep_ms(25);
|
|
}
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_stop_music(1.0);
|
|
#endif
|
|
|
|
// Break out of menu-game loop to shut down cleanly
|
|
if (device->run() == false || kill == true)
|
|
break;
|
|
|
|
infostream<<"Dropping main menu"<<std::endl;
|
|
|
|
menu->drop();
|
|
|
|
character_creator = menudata.character_creator;
|
|
|
|
if (character_creator)
|
|
break;
|
|
g_gamecallback->startgame_requested = false;
|
|
|
|
// Delete map if requested
|
|
if (menudata.delete_map) {
|
|
if (path_remove((char*)"world",NULL)) {
|
|
error_message = L"Map deletion failed";
|
|
continue;
|
|
}
|
|
if (menudata.use_fixed_seed)
|
|
config_set("world.map.seed",(char*)wide_to_narrow(menudata.fixed_seed).c_str());
|
|
config_set("world.map_type",(char*)menudata.map_type.c_str());
|
|
}else if (menudata.clear_map) {
|
|
if (path_remove((char*)"world",(char*)"map.sqlite")) {
|
|
error_message = L"Map clearing failed";
|
|
continue;
|
|
}
|
|
}
|
|
|
|
playername = wide_to_narrow(menudata.name);
|
|
|
|
password = translatePassword(playername, menudata.password);
|
|
|
|
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
|
|
|
|
config_set("world.server.address",(char*)wide_to_narrow(menudata.address).c_str());
|
|
int newport = mywstoi(menudata.port);
|
|
if (newport != 0)
|
|
config_set_int("world.server.port",newport);
|
|
config_set("world.game.mode", (char*)wide_to_narrow(menudata.game_mode).c_str());
|
|
config_set("world.game.mob.spawn.level", (char*)wide_to_narrow(menudata.max_mob_level).c_str());
|
|
config_set_int("world.player.inventory.starter", menudata.initial_inventory);
|
|
config_set_int("world.player.inventory.creative", menudata.infinite_inventory);
|
|
config_set_int("world.player.inventory.droppable", menudata.droppable_inventory);
|
|
config_set_int("world.player.inventory.keep", menudata.death_drops_inventory);
|
|
config_set_int("world.player.damage", menudata.enable_damage);
|
|
config_set_int("world.player.suffocation", menudata.suffocation);
|
|
config_set_int("world.player.hunger", menudata.hunger);
|
|
config_set_int("world.player.tool.wear", menudata.tool_wear);
|
|
config_set_int("world.game.environment.fire.spread", menudata.unsafe_fire);
|
|
|
|
// Save settings
|
|
config_set("client.name", (char*)playername.c_str());
|
|
config_set("world.server.admin",(char*)playername.c_str());
|
|
|
|
config_save(NULL,NULL,NULL);
|
|
|
|
// Continue to game
|
|
break;
|
|
}
|
|
|
|
// Break out of menu-game loop to shut down cleanly
|
|
if (device->run() == false)
|
|
break;
|
|
|
|
if (character_creator) {
|
|
character_creator = false;
|
|
video::IVideoDriver* driver = device->getVideoDriver();
|
|
|
|
GUICharDefMenu *menu = new GUICharDefMenu(device, guienv, guiroot, -1, &g_menumgr);
|
|
menu->allowFocusRemoval(true);
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_play_music("bg-charcreator",1.0,1);
|
|
#endif
|
|
|
|
while (device->run() && kill == false) {
|
|
if (menu->getStatus() == true)
|
|
break;
|
|
|
|
driver->beginScene(true, true, video::SColor(255,0,0,0));
|
|
|
|
smgr->drawAll();
|
|
guienv->drawAll();
|
|
|
|
driver->endScene();
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_step(0.02,NULL,NULL,NULL);
|
|
#endif
|
|
|
|
// On some computers framerate doesn't seem to be
|
|
// automatically limited
|
|
sleep_ms(25);
|
|
}
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_stop_music(1.0);
|
|
#endif
|
|
|
|
menu->drop();
|
|
|
|
continue;
|
|
}
|
|
// Initialize mapnode again to enable changed graphics settings
|
|
init_mapnode(device);
|
|
|
|
/*
|
|
Run game
|
|
*/
|
|
{
|
|
char* v = config_get("world.server.address");
|
|
if (!v || !v[0]) {
|
|
if (!world_init(NULL)) {
|
|
the_game(
|
|
kill,
|
|
input,
|
|
device,
|
|
font,
|
|
password,
|
|
error_message
|
|
);
|
|
world_exit();
|
|
}
|
|
}else{
|
|
the_game(
|
|
kill,
|
|
input,
|
|
device,
|
|
font,
|
|
password,
|
|
error_message
|
|
);
|
|
config_clear("world");
|
|
}
|
|
}
|
|
#if USE_AUDIO == 1
|
|
sound_stop_effects(0);
|
|
#endif
|
|
|
|
} //try
|
|
catch(con::PeerNotFoundException &e)
|
|
{
|
|
errorstream<<"Connection error (timed out?)"<<std::endl;
|
|
error_message = L"Connection error (timed out?)";
|
|
}
|
|
catch(SocketException &e)
|
|
{
|
|
errorstream<<"Socket error (port already in use?)"<<std::endl;
|
|
error_message = L"Socket error (port already in use?)";
|
|
}
|
|
#ifdef NDEBUG
|
|
catch(std::exception &e)
|
|
{
|
|
std::string narrow_message = "Some exception, what()=\"";
|
|
narrow_message += e.what();
|
|
narrow_message += "\"";
|
|
errorstream<<narrow_message<<std::endl;
|
|
error_message = narrow_to_wide(narrow_message);
|
|
}
|
|
#endif
|
|
|
|
} // Menu-game loop
|
|
|
|
config_save(NULL,NULL,NULL);
|
|
|
|
delete input;
|
|
|
|
#if USE_AUDIO == 1
|
|
sound_exit();
|
|
#endif
|
|
|
|
/*
|
|
In the end, delete the Irrlicht device.
|
|
*/
|
|
device->drop();
|
|
|
|
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
|
|
|
debugstreams_deinit();
|
|
|
|
return 0;
|
|
}
|
|
|
|
//END
|