mirror of
https://github.com/minetest/minetest.git
synced 2024-11-30 03:23:45 +01:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
e0564d5de0
@ -143,6 +143,7 @@ if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE})
|
||||
install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||
install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/")
|
||||
install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game")
|
||||
install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game")
|
||||
endif()
|
||||
set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build")
|
||||
if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE})
|
||||
|
20
README.txt
20
README.txt
@ -30,13 +30,17 @@ This game is not finished
|
||||
|
||||
Default Controls
|
||||
-----------------
|
||||
- WASD: Move
|
||||
- Space: Jump
|
||||
- E: Go down
|
||||
- Shift: Sneak
|
||||
- Q: Drop item
|
||||
- I: Open inventory
|
||||
- Mouse: Turn/look
|
||||
- WASD: move
|
||||
- Space: jump/climb
|
||||
- Shift: sneak/go down
|
||||
- Q: drop item
|
||||
- I: inventory
|
||||
- Mouse: turn/look
|
||||
- Mouse left: dig/punch
|
||||
- Mouse right: place/use
|
||||
- Mouse wheel: select item
|
||||
- Esc: pause menu
|
||||
- T: chat
|
||||
- Settable in the configuration file, see the section below.
|
||||
|
||||
Paths
|
||||
@ -277,7 +281,7 @@ the Free Software Foundation; either version 2.1 of the License, or
|
||||
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.
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
@ -1,11 +1,11 @@
|
||||
-- Minetest: builtin/features.lua
|
||||
|
||||
minetest.features = {
|
||||
"glasslike_framed" = true,
|
||||
"nodebox_as_selectionbox" = true,
|
||||
"chat_send_player_param3" = true,
|
||||
"get_all_craft_recipes_works" = true,
|
||||
"use_texture_alpha" = true,
|
||||
glasslike_framed = true,
|
||||
nodebox_as_selectionbox = true,
|
||||
chat_send_player_param3 = true,
|
||||
get_all_craft_recipes_works = true,
|
||||
use_texture_alpha = true,
|
||||
}
|
||||
|
||||
function minetest.has_feature(arg)
|
||||
|
@ -120,6 +120,17 @@ depends.txt:
|
||||
List of mods that have to be loaded before loading this mod.
|
||||
A single line contains a single modname.
|
||||
|
||||
Optional dependencies can be defined by appending a question mark
|
||||
to a single modname. Their meaning is that if the specified mod
|
||||
is missing, that does not prevent this mod from being loaded.
|
||||
|
||||
optdepends.txt:
|
||||
An alternative way of specifying optional dependencies.
|
||||
Like depends.txt, a single line contains a single modname.
|
||||
|
||||
NOTE: This file exists for compatibility purposes only and
|
||||
support for it will be removed from the engine by the end of 2013.
|
||||
|
||||
init.lua:
|
||||
The main Lua script. Running this script should register everything it
|
||||
wants to register. Subsequent execution depends on minetest calling the
|
||||
|
@ -61,6 +61,9 @@ Run dedicated server
|
||||
\-\-speedtests
|
||||
Run speed tests
|
||||
.TP
|
||||
\-\-videomodes
|
||||
List available video modes
|
||||
.TP
|
||||
\-\-info
|
||||
Print more information to console
|
||||
.TP
|
||||
|
BIN
games/minimal/menu/background.png
Normal file
BIN
games/minimal/menu/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
games/minimal/menu/icon.png
Normal file
BIN
games/minimal/menu/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 397 B |
@ -151,6 +151,8 @@
|
||||
#crosshair_color = (255,255,255)
|
||||
# Cross alpha (opaqueness, between 0 and 255)
|
||||
#crosshair_alpha = 255
|
||||
# Sensitivity multiplier
|
||||
#mouse_sensitivity = 0.2
|
||||
# Sound settings
|
||||
#enable_sound = true
|
||||
#sound_volume = 0.7
|
||||
|
@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "mapblock.h"
|
||||
#include "settings.h"
|
||||
#include "profiler.h"
|
||||
#include "gettext.h"
|
||||
#include "log.h"
|
||||
#include "nodemetadata.h"
|
||||
#include "nodedef.h"
|
||||
@ -2555,6 +2556,9 @@ void Client::inventoryAction(InventoryAction *a)
|
||||
Predict some local inventory changes
|
||||
*/
|
||||
a->clientApply(this, this);
|
||||
|
||||
// Remove it
|
||||
delete a;
|
||||
}
|
||||
|
||||
ClientActiveObject * Client::getSelectedActiveObject(
|
||||
@ -2801,7 +2805,10 @@ ClientEvent Client::getClientEvent()
|
||||
return m_client_event_queue.pop_front();
|
||||
}
|
||||
|
||||
void Client::afterContentReceived()
|
||||
void draw_load_screen(const std::wstring &text,
|
||||
IrrlichtDevice* device, gui::IGUIFont* font,
|
||||
float dtime=0 ,int percent=0, bool clouds=true);
|
||||
void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
|
||||
{
|
||||
infostream<<"Client::afterContentReceived() started"<<std::endl;
|
||||
assert(m_itemdef_received);
|
||||
@ -2836,13 +2843,23 @@ void Client::afterContentReceived()
|
||||
if(g_settings->getBool("preload_item_visuals"))
|
||||
{
|
||||
verbosestream<<"Updating item textures and meshes"<<std::endl;
|
||||
wchar_t* text = wgettext("Item textures...");
|
||||
draw_load_screen(text,device,font,0,0);
|
||||
std::set<std::string> names = m_itemdef->getAll();
|
||||
size_t size = names.size();
|
||||
size_t count = 0;
|
||||
int percent = 0;
|
||||
for(std::set<std::string>::const_iterator
|
||||
i = names.begin(); i != names.end(); ++i){
|
||||
// Asking for these caches the result
|
||||
m_itemdef->getInventoryTexture(*i, this);
|
||||
m_itemdef->getWieldMesh(*i, this);
|
||||
count++;
|
||||
percent = count*100/size;
|
||||
if (count%50 == 0) // only update every 50 item
|
||||
draw_load_screen(text,device,font,0,percent);
|
||||
}
|
||||
delete[] text;
|
||||
}
|
||||
|
||||
// Start mesh update thread after setting up content definitions
|
||||
|
@ -385,7 +385,7 @@ public:
|
||||
bool nodedefReceived()
|
||||
{ return m_nodedef_received; }
|
||||
|
||||
void afterContentReceived();
|
||||
void afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font);
|
||||
|
||||
float getRTT(void);
|
||||
|
||||
|
@ -91,10 +91,10 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
|
||||
PROTOCOL_VERSION 19:
|
||||
GENERIC_CMD_SET_PHYSICS_OVERRIDE
|
||||
PROTOCOL_VERSION 20:
|
||||
TOCLIENT_HUD_ADD
|
||||
TOCLIENT_HUD_RM
|
||||
TOCLIENT_HUD_CHANGE
|
||||
TOCLIENT_HUD_BUILTIN_ENABLE
|
||||
TOCLIENT_HUDADD
|
||||
TOCLIENT_HUDRM
|
||||
TOCLIENT_HUDCHANGE
|
||||
TOCLIENT_HUD_SET_FLAGS
|
||||
*/
|
||||
|
||||
#define LATEST_PROTOCOL_VERSION 20
|
||||
|
@ -117,6 +117,7 @@ void set_default_settings(Settings *settings)
|
||||
settings->setDefault("selectionbox_color", "(0,0,0)");
|
||||
settings->setDefault("crosshair_color", "(255,255,255)");
|
||||
settings->setDefault("crosshair_alpha", "255");
|
||||
settings->setDefault("mouse_sensitivity", "0.2");
|
||||
settings->setDefault("enable_sound", "true");
|
||||
settings->setDefault("sound_volume", "0.8");
|
||||
settings->setDefault("desynchronize_mapblock_texture_animation", "true");
|
||||
|
@ -1084,8 +1084,8 @@ void ServerEnvironment::step(float dtime)
|
||||
{
|
||||
v3s16 p = *i;
|
||||
|
||||
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
|
||||
<<") became inactive"<<std::endl;*/
|
||||
/* infostream<<"Server: Block " << PP(p)
|
||||
<< " became inactive"<<std::endl; */
|
||||
|
||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||
if(block==NULL)
|
||||
@ -1105,9 +1105,6 @@ void ServerEnvironment::step(float dtime)
|
||||
{
|
||||
v3s16 p = *i;
|
||||
|
||||
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
|
||||
<<") became active"<<std::endl;*/
|
||||
|
||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||
if(block==NULL){
|
||||
// Block needs to be fetched first
|
||||
@ -1117,6 +1114,8 @@ void ServerEnvironment::step(float dtime)
|
||||
}
|
||||
|
||||
activateBlock(block);
|
||||
/* infostream<<"Server: Block " << PP(p)
|
||||
<< " became active"<<std::endl; */
|
||||
}
|
||||
}
|
||||
|
||||
@ -1850,17 +1849,17 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
|
||||
<<" Forcing delete."<<std::endl;
|
||||
force_delete = true;
|
||||
} else {
|
||||
u16 new_id = pending_delete ? id : 0;
|
||||
// If static counterpart already exists, remove it first.
|
||||
// This shouldn't happen, but happens rarely for some
|
||||
// unknown reason. Unsuccessful attempts have been made to
|
||||
// find said reason.
|
||||
if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
|
||||
if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
|
||||
infostream<<"ServerEnv: WARNING: Performing hack #83274"
|
||||
<<std::endl;
|
||||
block->m_static_objects.remove(new_id);
|
||||
block->m_static_objects.remove(id);
|
||||
}
|
||||
block->m_static_objects.insert(new_id, s_obj);
|
||||
//store static data
|
||||
block->m_static_objects.insert(0, s_obj);
|
||||
|
||||
// Only mark block as modified if data changed considerably
|
||||
if(shall_be_written)
|
||||
|
302
src/game.cpp
302
src/game.cpp
@ -395,11 +395,14 @@ PointedThing getPointedThing(Client *client, v3f player_position,
|
||||
/*
|
||||
Draws a screen with a single text on it.
|
||||
Text will be removed when the screen is drawn the next time.
|
||||
Additionally, a progressbar can be drawn when percent is set between 0 and 100.
|
||||
*/
|
||||
/*gui::IGUIStaticText **/
|
||||
void draw_load_screen(const std::wstring &text,
|
||||
video::IVideoDriver* driver, gui::IGUIFont* font)
|
||||
IrrlichtDevice* device, gui::IGUIFont* font,
|
||||
float dtime=0 ,int percent=0, bool clouds=true)
|
||||
{
|
||||
video::IVideoDriver* driver = device->getVideoDriver();
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
const wchar_t *loadingtext = text.c_str();
|
||||
core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
|
||||
@ -411,7 +414,30 @@ void draw_load_screen(const std::wstring &text,
|
||||
loadingtext, textrect, false, false);
|
||||
guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
|
||||
|
||||
bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
|
||||
if (cloud_menu_background)
|
||||
{
|
||||
g_menuclouds->step(dtime*3);
|
||||
g_menuclouds->render();
|
||||
driver->beginScene(true, true, video::SColor(255,140,186,250));
|
||||
g_menucloudsmgr->drawAll();
|
||||
}
|
||||
else
|
||||
driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||
if (percent >= 0 && percent <= 100) // draw progress bar
|
||||
{
|
||||
core::vector2d<s32> barsize(256,32);
|
||||
core::rect<s32> barrect(center-barsize/2, center+barsize/2);
|
||||
driver->draw2DRectangle(video::SColor(255,255,255,255),barrect, NULL); // border
|
||||
driver->draw2DRectangle(video::SColor(255,64,64,64), core::rect<s32> (
|
||||
barrect.UpperLeftCorner+1,
|
||||
barrect.LowerRightCorner-1), NULL); // black inside the bar
|
||||
driver->draw2DRectangle(video::SColor(255,128,128,128), core::rect<s32> (
|
||||
barrect.UpperLeftCorner+1,
|
||||
core::vector2d<s32>(
|
||||
barrect.LowerRightCorner.X-(barsize.X-1)+percent*(barsize.X-2)/100,
|
||||
barrect.LowerRightCorner.Y-1)), NULL); // the actual progress
|
||||
}
|
||||
guienv->drawAll();
|
||||
driver->endScene();
|
||||
|
||||
@ -789,6 +815,67 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void nodePlacementPrediction(Client &client,
|
||||
const ItemDefinition &playeritem_def,
|
||||
v3s16 nodepos, v3s16 neighbourpos)
|
||||
{
|
||||
std::string prediction = playeritem_def.node_placement_prediction;
|
||||
INodeDefManager *nodedef = client.ndef();
|
||||
ClientMap &map = client.getEnv().getClientMap();
|
||||
|
||||
if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
|
||||
{
|
||||
verbosestream<<"Node placement prediction for "
|
||||
<<playeritem_def.name<<" is "
|
||||
<<prediction<<std::endl;
|
||||
v3s16 p = neighbourpos;
|
||||
// Place inside node itself if buildable_to
|
||||
try{
|
||||
MapNode n_under = map.getNode(nodepos);
|
||||
if(nodedef->get(n_under).buildable_to)
|
||||
p = nodepos;
|
||||
else if (!nodedef->get(map.getNode(p)).buildable_to)
|
||||
return;
|
||||
}catch(InvalidPositionException &e){}
|
||||
// Find id of predicted node
|
||||
content_t id;
|
||||
bool found = nodedef->getId(prediction, id);
|
||||
if(!found){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem_def.name<<" (places "
|
||||
<<prediction
|
||||
<<") - Name not known"<<std::endl;
|
||||
return;
|
||||
}
|
||||
// Predict param2
|
||||
u8 param2 = 0;
|
||||
if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
|
||||
v3s16 dir = nodepos - neighbourpos;
|
||||
if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
|
||||
param2 = dir.Y < 0 ? 1 : 0;
|
||||
} else if(abs(dir.X) > abs(dir.Z)){
|
||||
param2 = dir.X < 0 ? 3 : 2;
|
||||
} else {
|
||||
param2 = dir.Z < 0 ? 5 : 4;
|
||||
}
|
||||
}
|
||||
// TODO: Facedir prediction
|
||||
// TODO: If predicted node is in attached_node group, check attachment
|
||||
// Add node to client map
|
||||
MapNode n(id, 0, param2);
|
||||
try{
|
||||
// This triggers the required mesh update too
|
||||
client.addNode(p, n);
|
||||
}catch(InvalidPositionException &e){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem_def.name<<" (places "
|
||||
<<prediction
|
||||
<<") - Position not loaded"<<std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void the_game(
|
||||
bool &kill,
|
||||
bool random_input,
|
||||
@ -821,7 +908,11 @@ void the_game(
|
||||
Draw "Loading" screen
|
||||
*/
|
||||
|
||||
draw_load_screen(L"Loading...", driver, font);
|
||||
{
|
||||
wchar_t* text = wgettext("Loading...");
|
||||
draw_load_screen(text, device, font,0,0);
|
||||
delete[] text;
|
||||
}
|
||||
|
||||
// Create texture source
|
||||
IWritableTextureSource *tsrc = createTextureSource(device);
|
||||
@ -878,7 +969,9 @@ void the_game(
|
||||
*/
|
||||
|
||||
if(address == ""){
|
||||
draw_load_screen(L"Creating server...", driver, font);
|
||||
wchar_t* text = wgettext("Creating server....");
|
||||
draw_load_screen(text, device, font,0,25);
|
||||
delete[] text;
|
||||
infostream<<"Creating server"<<std::endl;
|
||||
server = new Server(map_dir, configpath, gamespec,
|
||||
simple_singleplayer_mode);
|
||||
@ -891,7 +984,11 @@ void the_game(
|
||||
Create client
|
||||
*/
|
||||
|
||||
draw_load_screen(L"Creating client...", driver, font);
|
||||
{
|
||||
wchar_t* text = wgettext("Creating client...");
|
||||
draw_load_screen(text, device, font,0,50);
|
||||
delete[] text;
|
||||
}
|
||||
infostream<<"Creating client"<<std::endl;
|
||||
|
||||
MapDrawControl draw_control;
|
||||
@ -902,7 +999,11 @@ void the_game(
|
||||
// Client acts as our GameDef
|
||||
IGameDef *gamedef = &client;
|
||||
|
||||
draw_load_screen(L"Resolving address...", driver, font);
|
||||
{
|
||||
wchar_t* text = wgettext("Resolving address...");
|
||||
draw_load_screen(text, device, font,0,75);
|
||||
delete[] text;
|
||||
}
|
||||
Address connect_address(0,0,0,0, port);
|
||||
try{
|
||||
if(address == "")
|
||||
@ -934,15 +1035,26 @@ void the_game(
|
||||
bool could_connect = false;
|
||||
bool connect_aborted = false;
|
||||
try{
|
||||
float frametime = 0.033;
|
||||
float time_counter = 0.0;
|
||||
input->clear();
|
||||
float fps_max = g_settings->getFloat("fps_max");
|
||||
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||
u32 lasttime = device->getTimer()->getTime();
|
||||
while(device->run())
|
||||
{
|
||||
f32 dtime=0; // in seconds
|
||||
if (cloud_menu_background) {
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
dtime = (time - lasttime) / 1000.0;
|
||||
else
|
||||
dtime = 0;
|
||||
lasttime = time;
|
||||
}
|
||||
// Update client and server
|
||||
client.step(frametime);
|
||||
client.step(dtime);
|
||||
if(server != NULL)
|
||||
server->step(frametime);
|
||||
server->step(dtime);
|
||||
|
||||
// End condition
|
||||
if(client.connectedAndInitialized()){
|
||||
@ -963,15 +1075,37 @@ void the_game(
|
||||
}
|
||||
|
||||
// Display status
|
||||
std::wostringstream ss;
|
||||
ss<<L"Connecting to server... (press Escape to cancel)\n";
|
||||
std::wstring animation = L"/-\\|";
|
||||
ss<<animation[(int)(time_counter/0.2)%4];
|
||||
draw_load_screen(ss.str(), driver, font);
|
||||
{
|
||||
wchar_t* text = wgettext("Connecting to server...");
|
||||
draw_load_screen(text, device, font, dtime, 100);
|
||||
delete[] text;
|
||||
}
|
||||
|
||||
// Delay a bit
|
||||
sleep_ms(1000*frametime);
|
||||
time_counter += frametime;
|
||||
// On some computers framerate doesn't seem to be
|
||||
// automatically limited
|
||||
if (cloud_menu_background) {
|
||||
// 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;
|
||||
|
||||
// FPS limiter
|
||||
u32 frametime_min = 1000./fps_max;
|
||||
|
||||
if(busytime_u32 < frametime_min) {
|
||||
u32 sleeptime = frametime_min - busytime_u32;
|
||||
device->sleep(sleeptime);
|
||||
}
|
||||
} else {
|
||||
sleep_ms(25);
|
||||
}
|
||||
time_counter += dtime;
|
||||
}
|
||||
}
|
||||
catch(con::PeerNotFoundException &e)
|
||||
@ -995,15 +1129,26 @@ void the_game(
|
||||
bool got_content = false;
|
||||
bool content_aborted = false;
|
||||
{
|
||||
float frametime = 0.033;
|
||||
float time_counter = 0.0;
|
||||
input->clear();
|
||||
float fps_max = g_settings->getFloat("fps_max");
|
||||
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||
u32 lasttime = device->getTimer()->getTime();
|
||||
while(device->run())
|
||||
{
|
||||
f32 dtime=0; // in seconds
|
||||
if (cloud_menu_background) {
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
dtime = (time - lasttime) / 1000.0;
|
||||
else
|
||||
dtime = 0;
|
||||
lasttime = time;
|
||||
}
|
||||
// Update client and server
|
||||
client.step(frametime);
|
||||
client.step(dtime);
|
||||
if(server != NULL)
|
||||
server->step(frametime);
|
||||
server->step(dtime);
|
||||
|
||||
// End condition
|
||||
if(client.texturesReceived() &&
|
||||
@ -1025,21 +1170,52 @@ void the_game(
|
||||
}
|
||||
|
||||
// Display status
|
||||
std::wostringstream ss;
|
||||
ss<<L"Waiting content... (press Escape to cancel)\n";
|
||||
std::ostringstream ss;
|
||||
int progress=0;
|
||||
if (!client.itemdefReceived())
|
||||
{
|
||||
ss << "Item definitions...";
|
||||
progress = 0;
|
||||
}
|
||||
else if (!client.nodedefReceived())
|
||||
{
|
||||
ss << "Node definitions...";
|
||||
progress = 25;
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "Media...";
|
||||
progress = 50+client.mediaReceiveProgress()*50+0.5;
|
||||
}
|
||||
wchar_t* text = wgettext(ss.str().c_str());
|
||||
draw_load_screen(text, device, font, dtime, progress);
|
||||
delete[] text;
|
||||
|
||||
ss<<(client.itemdefReceived()?L"[X]":L"[ ]");
|
||||
ss<<L" Item definitions\n";
|
||||
ss<<(client.nodedefReceived()?L"[X]":L"[ ]");
|
||||
ss<<L" Node definitions\n";
|
||||
ss<<L"["<<(int)(client.mediaReceiveProgress()*100+0.5)<<L"%] ";
|
||||
ss<<L" Media\n";
|
||||
// On some computers framerate doesn't seem to be
|
||||
// automatically limited
|
||||
if (cloud_menu_background) {
|
||||
// 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;
|
||||
|
||||
draw_load_screen(ss.str(), driver, font);
|
||||
// FPS limiter
|
||||
u32 frametime_min = 1000./fps_max;
|
||||
|
||||
// Delay a bit
|
||||
sleep_ms(1000*frametime);
|
||||
time_counter += frametime;
|
||||
if(busytime_u32 < frametime_min) {
|
||||
u32 sleeptime = frametime_min - busytime_u32;
|
||||
device->sleep(sleeptime);
|
||||
}
|
||||
} else {
|
||||
sleep_ms(25);
|
||||
}
|
||||
time_counter += dtime;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,7 +1232,7 @@ void the_game(
|
||||
After all content has been received:
|
||||
Update cached textures, meshes and materials
|
||||
*/
|
||||
client.afterContentReceived();
|
||||
client.afterContentReceived(device,font);
|
||||
|
||||
/*
|
||||
Create the camera node
|
||||
@ -1885,7 +2061,8 @@ void the_game(
|
||||
if(input->isKeyDown(irr::KEY_RIGHT))
|
||||
dx += dtime * keyspeed;*/
|
||||
|
||||
float d = 0.2;
|
||||
float d = g_settings->getFloat("mouse_sensitivity");
|
||||
d = rangelim(d, 0.01, 100.0);
|
||||
camera_yaw -= dx*d;
|
||||
camera_pitch += dy*d;
|
||||
if(camera_pitch < -89.5) camera_pitch = -89.5;
|
||||
@ -2198,17 +2375,15 @@ void the_game(
|
||||
- Can it point to liquids?
|
||||
*/
|
||||
ItemStack playeritem;
|
||||
bool playeritem_usable = false;
|
||||
bool playeritem_liquids_pointable = false;
|
||||
{
|
||||
InventoryList *mlist = local_inventory.getList("main");
|
||||
if(mlist != NULL)
|
||||
{
|
||||
playeritem = mlist->getItem(client.getPlayerItem());
|
||||
playeritem_usable = playeritem.getDefinition(itemdef).usable;
|
||||
playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable;
|
||||
}
|
||||
}
|
||||
const ItemDefinition &playeritem_def =
|
||||
playeritem.getDefinition(itemdef);
|
||||
ToolCapabilities playeritem_toolcap =
|
||||
playeritem.getToolCapabilities(itemdef);
|
||||
|
||||
@ -2267,7 +2442,7 @@ void the_game(
|
||||
// input
|
||||
&client, player_position, camera_direction,
|
||||
camera_position, shootline, d,
|
||||
playeritem_liquids_pointable, !ldown_for_dig,
|
||||
playeritem_def.liquids_pointable, !ldown_for_dig,
|
||||
// output
|
||||
hilightboxes,
|
||||
selected_object);
|
||||
@ -2327,7 +2502,7 @@ void the_game(
|
||||
else
|
||||
repeat_rightclick_timer = 0;
|
||||
|
||||
if(playeritem_usable && input->getLeftState())
|
||||
if(playeritem_def.usable && input->getLeftState())
|
||||
{
|
||||
if(input->getLeftClicked())
|
||||
client.interact(4, pointed);
|
||||
@ -2534,46 +2709,13 @@ void the_game(
|
||||
|
||||
// If the wielded item has node placement prediction,
|
||||
// make that happen
|
||||
const ItemDefinition &def =
|
||||
playeritem.getDefinition(itemdef);
|
||||
if(def.node_placement_prediction != ""
|
||||
&& !nodedef->get(map.getNode(nodepos)).rightclickable)
|
||||
do{ // breakable
|
||||
verbosestream<<"Node placement prediction for "
|
||||
<<playeritem.name<<" is "
|
||||
<<def.node_placement_prediction<<std::endl;
|
||||
v3s16 p = neighbourpos;
|
||||
// Place inside node itself if buildable_to
|
||||
try{
|
||||
MapNode n_under = map.getNode(nodepos);
|
||||
if(nodedef->get(n_under).buildable_to)
|
||||
p = nodepos;
|
||||
}catch(InvalidPositionException &e){}
|
||||
// Find id of predicted node
|
||||
content_t id;
|
||||
bool found =
|
||||
nodedef->getId(def.node_placement_prediction, id);
|
||||
if(!found){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem.name<<" (places "
|
||||
<<def.node_placement_prediction
|
||||
<<") - Name not known"<<std::endl;
|
||||
break;
|
||||
}
|
||||
MapNode n(id);
|
||||
try{
|
||||
// This triggers the required mesh update too
|
||||
client.addNode(p, n);
|
||||
}catch(InvalidPositionException &e){
|
||||
errorstream<<"Node placement prediction failed for "
|
||||
<<playeritem.name<<" (places "
|
||||
<<def.node_placement_prediction
|
||||
<<") - Position not loaded"<<std::endl;
|
||||
}
|
||||
}while(0);
|
||||
nodePlacementPrediction(client,
|
||||
playeritem_def,
|
||||
nodepos, neighbourpos);
|
||||
|
||||
// Read the sound
|
||||
soundmaker.m_player_rightpunch_sound = def.sound_place;
|
||||
soundmaker.m_player_rightpunch_sound =
|
||||
playeritem_def.sound_place;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3193,6 +3335,8 @@ void the_game(
|
||||
clouds->drop();
|
||||
if (gui_chat_console)
|
||||
gui_chat_console->drop();
|
||||
if (sky)
|
||||
sky->drop();
|
||||
clear_particles();
|
||||
|
||||
/*
|
||||
@ -3201,7 +3345,9 @@ void the_game(
|
||||
*/
|
||||
{
|
||||
/*gui::IGUIStaticText *gui_shuttingdowntext = */
|
||||
draw_load_screen(L"Shutting down stuff...", driver, font);
|
||||
wchar_t* text = wgettext("Shutting down stuff...");
|
||||
draw_load_screen(text, device, font, 0, -1, false);
|
||||
delete[] text;
|
||||
/*driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||
guienv->drawAll();
|
||||
driver->endScene();
|
||||
|
@ -121,6 +121,7 @@ GUIChatConsole::GUIChatConsole(
|
||||
|
||||
GUIChatConsole::~GUIChatConsole()
|
||||
{
|
||||
delete m_font;
|
||||
}
|
||||
|
||||
void GUIChatConsole::openConsole(f32 height)
|
||||
|
@ -407,6 +407,8 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event)
|
||||
delete[] text;
|
||||
menu->drop();
|
||||
|
||||
try
|
||||
{
|
||||
ModConfiguration modconf(m_wspec.path);
|
||||
if(!modconf.isConsistent())
|
||||
{
|
||||
@ -417,6 +419,16 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event)
|
||||
delete[] text;
|
||||
menu->drop();
|
||||
}
|
||||
}
|
||||
catch(ModError &err)
|
||||
{
|
||||
errorstream<<err.what()<<std::endl;
|
||||
std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
|
||||
GUIMessageMenu *menu =
|
||||
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||
text );
|
||||
menu->drop();
|
||||
}
|
||||
|
||||
quitMenu();
|
||||
return true;
|
||||
|
@ -42,13 +42,22 @@ GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
CreateWorldDest *dest,
|
||||
const std::vector<SubgameSpec> &games
|
||||
const std::vector<SubgameSpec> &games,
|
||||
const std::string &initial_game
|
||||
):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_dest(dest),
|
||||
m_games(games)
|
||||
m_games(games),
|
||||
m_initial_game_i(0)
|
||||
{
|
||||
assert(games.size() > 0);
|
||||
|
||||
for(size_t i=0; i<games.size(); i++){
|
||||
if(games[i].id == initial_game){
|
||||
m_initial_game_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUICreateWorld::~GUICreateWorld()
|
||||
@ -151,7 +160,7 @@ void GUICreateWorld::regenerateGui(v2u32 screensize)
|
||||
os<<L"]";
|
||||
e->addItem(os.str().c_str());
|
||||
}
|
||||
e->setSelected(0);
|
||||
e->setSelected(m_initial_game_i);
|
||||
}
|
||||
changeCtype("");
|
||||
{
|
||||
|
@ -38,7 +38,8 @@ public:
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
CreateWorldDest *dest,
|
||||
const std::vector<SubgameSpec> &games);
|
||||
const std::vector<SubgameSpec> &games,
|
||||
const std::string &initial_game);
|
||||
~GUICreateWorld();
|
||||
|
||||
void removeChildren();
|
||||
@ -56,6 +57,7 @@ public:
|
||||
private:
|
||||
CreateWorldDest *m_dest;
|
||||
std::vector<SubgameSpec> m_games;
|
||||
int m_initial_game_i;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -53,12 +53,12 @@ const wchar_t *contrib_core_strs[] = {
|
||||
L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>",
|
||||
L"Lisa Milne (darkrose) <lisa@ltmnet.com>",
|
||||
L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>",
|
||||
L"proller <proler@gmail.com>"
|
||||
L"proller <proler@gmail.com>",
|
||||
L"sfan5 <sfan5@live.de>"
|
||||
};
|
||||
|
||||
const wchar_t *contrib_active_strs[] = {
|
||||
L"kahrl <kahrl@gmx.net>",
|
||||
L"sfan5 <sfan5@live.de>",
|
||||
L"sapier <sapier@gmx.net>",
|
||||
L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>",
|
||||
L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
|
||||
@ -162,15 +162,8 @@ enum
|
||||
GUI_ID_SERVERLIST_TOGGLE,
|
||||
GUI_ID_SERVERLIST_DELETE,
|
||||
GUI_ID_SERVERLIST_TITLE,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TAB_SINGLEPLAYER=0,
|
||||
TAB_MULTIPLAYER,
|
||||
TAB_ADVANCED,
|
||||
TAB_SETTINGS,
|
||||
TAB_CREDITS
|
||||
GUI_ID_GAME_BUTTON_FIRST = 130,
|
||||
GUI_ID_GAME_BUTTON_MAX = 150,
|
||||
};
|
||||
|
||||
GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env,
|
||||
@ -255,8 +248,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
core::rect<s32> rect(0, 0, size.X, 40);
|
||||
rect += v2s32(4, 0);
|
||||
Environment->addStaticText(narrow_to_wide(
|
||||
"Minetest " VERSION_STRING).c_str(),
|
||||
std::string t = "Minetest " VERSION_STRING;
|
||||
if(m_data->selected_game_name != "" &&
|
||||
m_data->selected_tab == TAB_SINGLEPLAYER){
|
||||
t += "/";
|
||||
t += m_data->selected_game_name;
|
||||
}
|
||||
Environment->addStaticText(narrow_to_wide(t).c_str(),
|
||||
rect, false, true, this, -1);
|
||||
}
|
||||
|
||||
@ -342,11 +340,17 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
||||
GUI_ID_WORLD_LISTBOX);
|
||||
e->setDrawBackground(true);
|
||||
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
|
||||
i != m_data->worlds.end(); i++){
|
||||
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
|
||||
m_world_indices.clear();
|
||||
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
|
||||
const WorldSpec &spec = m_data->worlds[wi];
|
||||
if(spec.gameid == m_data->selected_game){
|
||||
//e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
|
||||
e->addItem(narrow_to_wide(spec.name).c_str());
|
||||
m_world_indices.push_back(wi);
|
||||
if(m_data->selected_world == (int)wi)
|
||||
e->setSelected(m_world_indices.size()-1);
|
||||
}
|
||||
}
|
||||
e->setSelected(m_data->selected_world);
|
||||
Environment->setFocus(e);
|
||||
}
|
||||
// Delete world button
|
||||
@ -416,6 +420,26 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
delete[] text;
|
||||
}
|
||||
changeCtype("C");
|
||||
|
||||
/* Add game selection buttons */
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
for(size_t i=0; i<m_data->games.size(); i++){
|
||||
const SubgameSpec *spec = &m_data->games[i];
|
||||
v2s32 p(8 + i*(48+8), screensize.Y - (48+8));
|
||||
core::rect<s32> rect(0, 0, 48, 48);
|
||||
rect += p;
|
||||
video::ITexture *bgtexture = NULL;
|
||||
if(spec->menuicon_path != "")
|
||||
bgtexture = driver->getTexture(spec->menuicon_path.c_str());
|
||||
gui::IGUIButton *b = Environment->addButton(rect, this,
|
||||
GUI_ID_GAME_BUTTON_FIRST+i, narrow_to_wide(wrap_rows(spec->id, 4)).c_str());
|
||||
if(bgtexture){
|
||||
b->setImage(bgtexture);
|
||||
b->setText(L"");
|
||||
b->setDrawBorder(false);
|
||||
b->setUseAlphaChannel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(m_data->selected_tab == TAB_MULTIPLAYER)
|
||||
{
|
||||
@ -471,16 +495,20 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 390, 20);
|
||||
rect += m_topleft_client + v2s32(50, 10);
|
||||
Environment->addStaticText(wgettext("Favorites:"),
|
||||
wchar_t* text = wgettext("Favorites:");
|
||||
Environment->addStaticText(text,
|
||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||
delete[] text;
|
||||
}
|
||||
} else {
|
||||
m_data->servers = ServerList::getOnline();
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 390, 20);
|
||||
rect += m_topleft_client + v2s32(50, 10);
|
||||
Environment->addStaticText(wgettext("Public Server List:"),
|
||||
wchar_t* text = wgettext("Public Server List:");
|
||||
Environment->addStaticText(text,
|
||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||
delete[] text;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -488,8 +516,10 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 390, 20);
|
||||
rect += m_topleft_client + v2s32(50, 10);
|
||||
Environment->addStaticText(wgettext("Favorites:"),
|
||||
wchar_t* text = wgettext("Favorites:");
|
||||
Environment->addStaticText(text,
|
||||
rect, false, true, this, GUI_ID_SERVERLIST_TITLE);
|
||||
delete[] text;
|
||||
}
|
||||
#endif
|
||||
updateGuiServerList();
|
||||
@ -714,9 +744,11 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
|
||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
||||
GUI_ID_WORLD_LISTBOX);
|
||||
e->setDrawBackground(true);
|
||||
for(std::vector<WorldSpec>::const_iterator i = m_data->worlds.begin();
|
||||
i != m_data->worlds.end(); i++){
|
||||
e->addItem(narrow_to_wide(i->name+" ["+i->gameid+"]").c_str());
|
||||
m_world_indices.clear();
|
||||
for(size_t wi = 0; wi < m_data->worlds.size(); wi++){
|
||||
const WorldSpec &spec = m_data->worlds[wi];
|
||||
e->addItem(narrow_to_wide(spec.name+" ["+spec.gameid+"]").c_str());
|
||||
m_world_indices.push_back(wi);
|
||||
}
|
||||
e->setSelected(m_data->selected_world);
|
||||
}
|
||||
@ -910,6 +942,8 @@ void GUIMainMenu::drawMenu()
|
||||
return;
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
|
||||
/* Draw menu background */
|
||||
|
||||
/*video::SColor bgcolor(140,0,0,0);
|
||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);*/
|
||||
|
||||
@ -976,6 +1010,8 @@ void GUIMainMenu::drawMenu()
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw UI elements */
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
@ -1100,8 +1136,13 @@ void GUIMainMenu::readInput(MainMenuData *dst)
|
||||
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX);
|
||||
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
|
||||
dst->selected_world = ((gui::IGUIListBox*)e)->getSelected();
|
||||
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX){
|
||||
int list_i = ((gui::IGUIListBox*)e)->getSelected();
|
||||
if(list_i == -1)
|
||||
dst->selected_world = -1;
|
||||
else
|
||||
dst->selected_world = m_world_indices[list_i];
|
||||
}
|
||||
}
|
||||
{
|
||||
ServerListSpec server =
|
||||
@ -1221,7 +1262,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
||||
return true;
|
||||
}
|
||||
case GUI_ID_CREATE_WORLD_BUTTON: {
|
||||
std::vector<SubgameSpec> games = getAvailableGames();
|
||||
const std::vector<SubgameSpec> &games = m_data->games;
|
||||
if(games.size() == 0){
|
||||
wchar_t* text = wgettext("Cannot create world: No games found");
|
||||
GUIMessageMenu *menu = new GUIMessageMenu(env, parent,
|
||||
@ -1232,7 +1273,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
||||
} else {
|
||||
CreateWorldDest *dest = new CreateWorldDestMainMenu(this);
|
||||
GUICreateWorld *menu = new GUICreateWorld(env, parent, -1,
|
||||
menumgr, dest, games);
|
||||
menumgr, dest, games, m_data->selected_game);
|
||||
menu->drop();
|
||||
}
|
||||
return true;
|
||||
@ -1308,6 +1349,16 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Game buttons */
|
||||
int eid = event.GUIEvent.Caller->getID();
|
||||
if(eid >= GUI_ID_GAME_BUTTON_FIRST &&
|
||||
eid <= GUI_ID_GAME_BUTTON_MAX){
|
||||
m_data->selected_game =
|
||||
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].id;
|
||||
m_data->selected_game_name =
|
||||
m_data->games[eid - GUI_ID_GAME_BUTTON_FIRST].name;
|
||||
regenerateGui(m_screensize_old);
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
|
||||
{
|
||||
@ -1318,9 +1369,11 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
||||
readInput(&cur);
|
||||
if (getTab() == TAB_MULTIPLAYER && cur.address == L"")
|
||||
{
|
||||
wchar_t* text = wgettext("Address required.");
|
||||
(new GUIMessageMenu(env, parent, -1, menumgr,
|
||||
wgettext("Address required."))
|
||||
text)
|
||||
)->drop();
|
||||
delete[] text;
|
||||
return true;
|
||||
}
|
||||
acceptInput();
|
||||
@ -1328,6 +1381,10 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED)
|
||||
{
|
||||
readInput(m_data);
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
|
||||
{
|
||||
switch(event.GUIEvent.Caller->getID())
|
||||
|
@ -34,11 +34,22 @@ enum {
|
||||
SERVERLIST_PUBLIC,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
TAB_SINGLEPLAYER=0,
|
||||
TAB_MULTIPLAYER,
|
||||
TAB_ADVANCED,
|
||||
TAB_SETTINGS,
|
||||
TAB_CREDITS
|
||||
};
|
||||
|
||||
struct MainMenuData
|
||||
{
|
||||
// These are in the native format of the gui elements
|
||||
// Generic
|
||||
int selected_tab;
|
||||
std::string selected_game;
|
||||
std::string selected_game_name;
|
||||
// Client options
|
||||
std::string servername;
|
||||
std::string serverdescription;
|
||||
@ -78,6 +89,8 @@ struct MainMenuData
|
||||
MainMenuData():
|
||||
// Generic
|
||||
selected_tab(0),
|
||||
selected_game("minetest"),
|
||||
selected_game_name("Minetest"),
|
||||
// Client opts
|
||||
fancy_trees(false),
|
||||
smooth_lighting(false),
|
||||
@ -128,6 +141,8 @@ private:
|
||||
s32 id;
|
||||
IMenuManager *menumgr;
|
||||
|
||||
std::vector<int> m_world_indices;
|
||||
|
||||
bool m_is_regenerating;
|
||||
v2s32 m_topleft_client;
|
||||
v2s32 m_size_client;
|
||||
|
@ -168,16 +168,16 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
|
||||
core::rect<s32> rect(0, 0, 180, 240);
|
||||
rect = rect + v2s32(size.X/2 + 90, size.Y/2-rect.getHeight()/2);
|
||||
wchar_t* text = wgettext("Default Controls:\n"
|
||||
"- WASD: Walk\n"
|
||||
"- Mouse left: dig/hit\n"
|
||||
"- WASD: move\n"
|
||||
"- Space: jump/climb\n"
|
||||
"- Shift: sneak/go down\n"
|
||||
"- Q: drop item\n"
|
||||
"- I: inventory\n"
|
||||
"- Mouse: turn/look\n"
|
||||
"- Mouse left: dig/punch\n"
|
||||
"- Mouse right: place/use\n"
|
||||
"- Mouse wheel: select item\n"
|
||||
"- 0...9: select item\n"
|
||||
"- Shift: sneak\n"
|
||||
"- R: Toggle viewing all loaded chunks\n"
|
||||
"- I: Inventory menu\n"
|
||||
"- ESC: This menu\n"
|
||||
"- T: Chat\n"
|
||||
"- T: chat\n"
|
||||
);
|
||||
Environment->addStaticText(text, rect, false, true, this, 258);
|
||||
delete[] text;
|
||||
|
@ -226,17 +226,11 @@ class CItemDefManager: public IWritableItemDefManager
|
||||
public:
|
||||
CItemDefManager()
|
||||
{
|
||||
for (std::map<std::string, ItemDefinition*>::iterator iter =
|
||||
m_item_definitions.begin(); iter != m_item_definitions.end();
|
||||
iter ++) {
|
||||
delete iter->second;
|
||||
}
|
||||
m_item_definitions.clear();
|
||||
|
||||
#ifndef SERVER
|
||||
m_main_thread = get_current_thread_id();
|
||||
m_driver = NULL;
|
||||
#endif
|
||||
|
||||
clear();
|
||||
}
|
||||
virtual ~CItemDefManager()
|
||||
@ -260,6 +254,12 @@ public:
|
||||
}
|
||||
m_driver = NULL;
|
||||
#endif
|
||||
for (std::map<std::string, ItemDefinition*>::iterator iter =
|
||||
m_item_definitions.begin(); iter != m_item_definitions.end();
|
||||
iter ++) {
|
||||
delete iter->second;
|
||||
}
|
||||
m_item_definitions.clear();
|
||||
}
|
||||
virtual const ItemDefinition& get(const std::string &name_) const
|
||||
{
|
||||
|
421
src/main.cpp
421
src/main.cpp
@ -76,6 +76,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "subgame.h"
|
||||
#include "quicktune.h"
|
||||
#include "serverlist.h"
|
||||
#include "sound.h"
|
||||
#include "sound_openal.h"
|
||||
|
||||
/*
|
||||
Settings.
|
||||
@ -88,6 +90,10 @@ Settings *g_settings = &main_settings;
|
||||
Profiler main_profiler;
|
||||
Profiler *g_profiler = &main_profiler;
|
||||
|
||||
// Menu clouds are created later
|
||||
Clouds *g_menuclouds = 0;
|
||||
irr::scene::ISceneManager *g_menucloudsmgr = 0;
|
||||
|
||||
/*
|
||||
Debug streams
|
||||
*/
|
||||
@ -196,7 +202,49 @@ u32 getTime(TimePrecision prec) {
|
||||
return 0;
|
||||
return g_timegetter->getTime(prec);
|
||||
}
|
||||
#endif
|
||||
|
||||
//Client side main menu music fetcher
|
||||
#ifndef SERVER
|
||||
class MenuMusicFetcher: public OnDemandSoundFetcher
|
||||
{
|
||||
std::set<std::string> m_fetched;
|
||||
public:
|
||||
|
||||
void fetchSounds(const std::string &name,
|
||||
std::set<std::string> &dst_paths,
|
||||
std::set<std::string> &dst_datas)
|
||||
{
|
||||
if(m_fetched.count(name))
|
||||
return;
|
||||
m_fetched.insert(name);
|
||||
std::string base;
|
||||
base = porting::path_share + DIR_DELIM + "sounds";
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
|
||||
base = porting::path_user + DIR_DELIM + "sounds";
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
|
||||
dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class StderrLogOutput: public ILogOutput
|
||||
@ -612,81 +660,124 @@ private:
|
||||
bool rightreleased;
|
||||
};
|
||||
|
||||
//Draw the tiled menu background
|
||||
void drawMenuBackground(video::IVideoDriver* driver) {
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
struct MenuTextures
|
||||
{
|
||||
std::string current_gameid;
|
||||
bool global_textures;
|
||||
video::ITexture *background;
|
||||
video::ITexture *overlay;
|
||||
video::ITexture *header;
|
||||
video::ITexture *footer;
|
||||
|
||||
std::string path = getTexturePath("menubg.png");
|
||||
if (path[0]) {
|
||||
static const video::ITexture *bgtexture =
|
||||
driver->getTexture(path.c_str());
|
||||
MenuTextures():
|
||||
global_textures(false),
|
||||
background(NULL),
|
||||
overlay(NULL),
|
||||
header(NULL),
|
||||
footer(NULL)
|
||||
{}
|
||||
|
||||
if (bgtexture) {
|
||||
s32 scaledsize = 128;
|
||||
static video::ITexture* getMenuTexture(const std::string &tname,
|
||||
video::IVideoDriver* driver, const SubgameSpec *spec)
|
||||
{
|
||||
if(spec){
|
||||
std::string path;
|
||||
// eg. minetest_menu_background.png (for texture packs)
|
||||
std::string pack_tname = spec->id + "_menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
// eg. games/minetest_game/menu/background.png
|
||||
path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
} else {
|
||||
std::string path;
|
||||
// eg. menu_background.png
|
||||
std::string pack_tname = "menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The important difference between destsize and screensize is
|
||||
// that destsize is rounded to whole scaled pixels.
|
||||
// These formulas use component-wise multiplication and division of v2u32.
|
||||
v2u32 texturesize = bgtexture->getSize();
|
||||
v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1);
|
||||
v2u32 destsize = scaledsize * sourcesize / texturesize;
|
||||
void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
|
||||
{
|
||||
if(tab == TAB_SINGLEPLAYER){
|
||||
if(spec->id == current_gameid)
|
||||
return;
|
||||
current_gameid = spec->id;
|
||||
global_textures = false;
|
||||
background = getMenuTexture("background", driver, spec);
|
||||
overlay = getMenuTexture("overlay", driver, spec);
|
||||
header = getMenuTexture("header", driver, spec);
|
||||
footer = getMenuTexture("footer", driver, spec);
|
||||
} else {
|
||||
if(global_textures)
|
||||
return;
|
||||
current_gameid = "";
|
||||
global_textures = true;
|
||||
background = getMenuTexture("background", driver, NULL);
|
||||
overlay = getMenuTexture("overlay", driver, NULL);
|
||||
header = getMenuTexture("header", driver, NULL);
|
||||
footer = getMenuTexture("footer", driver, NULL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Default texture wrapping mode in Irrlicht is ETC_REPEAT.
|
||||
driver->draw2DImage(bgtexture,
|
||||
core::rect<s32>(0, 0, destsize.X, destsize.Y),
|
||||
void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.background;
|
||||
|
||||
/* If no texture, draw background of solid color */
|
||||
if(!texture){
|
||||
video::SColor color(255,80,58,37);
|
||||
core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
|
||||
driver->draw2DRectangle(color, rect, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Draw background texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Draw the footer at the bottom of the window
|
||||
void drawMenuFooter(video::IVideoDriver* driver, bool clouds) {
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
std::string path = getTexturePath(clouds ?
|
||||
"menufooter_clouds.png" : "menufooter.png");
|
||||
if (path[0]) {
|
||||
static const video::ITexture *footertexture =
|
||||
driver->getTexture(path.c_str());
|
||||
void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.overlay;
|
||||
|
||||
if (footertexture) {
|
||||
f32 mult = (((f32)screensize.Width)) /
|
||||
((f32)footertexture->getOriginalSize().Width);
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult,
|
||||
((f32)footertexture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the footer if there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
if (free_space > footersize.Y) {
|
||||
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
|
||||
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
||||
rect -= v2s32(footersize.X/2, 0);
|
||||
|
||||
driver->draw2DImage(footertexture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(footertexture->getSize())),
|
||||
/* Draw overlay texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the Header over the main menu
|
||||
void drawMenuHeader(video::IVideoDriver* driver) {
|
||||
void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.header;
|
||||
|
||||
std::string path = getTexturePath("menuheader.png");
|
||||
if (path[0]) {
|
||||
static const video::ITexture *splashtexture =
|
||||
driver->getTexture(path.c_str());
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
if(splashtexture) {
|
||||
f32 mult = (((f32)screensize.Width / 2)) /
|
||||
((f32)splashtexture->getOriginalSize().Width);
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult,
|
||||
((f32)splashtexture->getOriginalSize().Height) * mult);
|
||||
v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the header is there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
@ -697,37 +788,53 @@ void drawMenuHeader(video::IVideoDriver* driver) {
|
||||
|
||||
video::SColor bgcolor(255,50,50,50);
|
||||
|
||||
driver->draw2DImage(splashtexture, splashrect,
|
||||
driver->draw2DImage(texture, splashrect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(splashtexture->getSize())),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the Splash over the clouds and under the main menu
|
||||
void drawMenuSplash(video::IVideoDriver* driver) {
|
||||
void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
std::string path = getTexturePath("menusplash.png");
|
||||
if (path[0]) {
|
||||
static const video::ITexture *splashtexture =
|
||||
driver->getTexture(path.c_str());
|
||||
video::ITexture *texture = menutextures.footer;
|
||||
|
||||
if(splashtexture) {
|
||||
core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height);
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
video::SColor bgcolor(255,50,50,50);
|
||||
f32 mult = (((f32)screensize.Width)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
driver->draw2DImage(splashtexture, splashrect,
|
||||
v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the footer if there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
if (free_space > footersize.Y) {
|
||||
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
|
||||
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
||||
rect -= v2s32(footersize.X/2, 0);
|
||||
|
||||
driver->draw2DImage(texture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(splashtexture->getSize())),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
|
||||
{
|
||||
for(size_t i=0; i<menudata.games.size(); i++){
|
||||
if(menudata.games[i].id == menudata.selected_game){
|
||||
return &menudata.games[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // !SERVER
|
||||
|
||||
// These are defined global so that they're not optimized too much.
|
||||
// Can't change them to volatile.
|
||||
@ -900,6 +1007,8 @@ int main(int argc, char *argv[])
|
||||
allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING,
|
||||
_("Set gameid (\"--gameid list\" prints available ones)"))));
|
||||
#ifndef SERVER
|
||||
allowed_options.insert(std::make_pair("videomodes", ValueSpec(VALUETYPE_FLAG,
|
||||
_("Show available video modes"))));
|
||||
allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
|
||||
_("Run speed tests"))));
|
||||
allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
|
||||
@ -1379,11 +1488,64 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
/*
|
||||
Create device and exit if creation failed
|
||||
List video modes if requested
|
||||
*/
|
||||
|
||||
MyEventReceiver receiver;
|
||||
|
||||
if(cmd_args.getFlag("videomodes")){
|
||||
IrrlichtDevice *nulldevice;
|
||||
|
||||
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
||||
params.DriverType = video::EDT_NULL;
|
||||
params.WindowSize = core::dimension2d<u32>(640, 480);
|
||||
params.Bits = 24;
|
||||
params.AntiAlias = fsaa;
|
||||
params.Fullscreen = false;
|
||||
params.Stencilbuffer = false;
|
||||
params.Vsync = vsync;
|
||||
params.EventReceiver = &receiver;
|
||||
|
||||
nulldevice = createDeviceEx(params);
|
||||
|
||||
if(nulldevice == 0)
|
||||
return 1;
|
||||
|
||||
dstream<<_("Available video modes (WxHxD):")<<std::endl;
|
||||
|
||||
video::IVideoModeList *videomode_list =
|
||||
nulldevice->getVideoModeList();
|
||||
|
||||
if(videomode_list == 0){
|
||||
nulldevice->drop();
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 videomode_count = videomode_list->getVideoModeCount();
|
||||
core::dimension2d<u32> videomode_res;
|
||||
s32 videomode_depth;
|
||||
for (s32 i = 0; i < videomode_count; ++i){
|
||||
videomode_res = videomode_list->getVideoModeResolution(i);
|
||||
videomode_depth = videomode_list->getVideoModeDepth(i);
|
||||
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
|
||||
<<"x"<<videomode_depth<<std::endl;
|
||||
}
|
||||
|
||||
dstream<<_("Active video mode (WxHxD):")<<std::endl;
|
||||
videomode_res = videomode_list->getDesktopResolution();
|
||||
videomode_depth = videomode_list->getDesktopDepth();
|
||||
dstream<<videomode_res.Width<<"x"<<videomode_res.Height
|
||||
<<"x"<<videomode_depth<<std::endl;
|
||||
|
||||
nulldevice->drop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Create device and exit if creation failed
|
||||
*/
|
||||
|
||||
IrrlichtDevice *device;
|
||||
|
||||
SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
|
||||
@ -1426,6 +1588,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
dstream<<"Running speed tests"<<std::endl;
|
||||
SpeedTests();
|
||||
device->drop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1477,6 +1640,19 @@ int main(int argc, char *argv[])
|
||||
skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255,96,134,49));
|
||||
#endif
|
||||
|
||||
|
||||
// Create the menu clouds
|
||||
if (!g_menucloudsmgr)
|
||||
g_menucloudsmgr = smgr->createNewSceneManager();
|
||||
if (!g_menuclouds)
|
||||
g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
|
||||
g_menucloudsmgr, -1, rand(), 100);
|
||||
g_menuclouds->update(v2f(0, 0), video::SColor(255,200,200,255));
|
||||
scene::ICameraSceneNode* camera;
|
||||
camera = g_menucloudsmgr->addCameraSceneNode(0,
|
||||
v3f(0,0,0), v3f(0, 60, 100));
|
||||
camera->setFarValue(10000);
|
||||
|
||||
/*
|
||||
GUI stuff
|
||||
*/
|
||||
@ -1558,6 +1734,10 @@ int main(int argc, char *argv[])
|
||||
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
|
||||
if(g_settings->exists("selected_serverlist"))
|
||||
menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
|
||||
if(g_settings->exists("selected_mainmenu_game")){
|
||||
menudata.selected_game = g_settings->get("selected_mainmenu_game");
|
||||
menudata.selected_game_name = findSubgame(menudata.selected_game).name;
|
||||
}
|
||||
menudata.address = narrow_to_wide(address);
|
||||
menudata.name = narrow_to_wide(playername);
|
||||
menudata.port = narrow_to_wide(itos(port));
|
||||
@ -1611,6 +1791,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
// Copy worldspecs to menu
|
||||
menudata.worlds = worldspecs;
|
||||
// Get game listing
|
||||
menudata.games = getAvailableGames();
|
||||
// If selected game doesn't exist, take first from list
|
||||
if(findSubgame(menudata.selected_game).id == "" &&
|
||||
!menudata.games.empty()){
|
||||
menudata.selected_game = menudata.games[0].id;
|
||||
}
|
||||
const SubgameSpec *menugame = getMenuGame(menudata);
|
||||
|
||||
MenuTextures menutextures;
|
||||
menutextures.update(driver, menugame, menudata.selected_tab);
|
||||
|
||||
if(skip_main_menu == false)
|
||||
{
|
||||
@ -1623,7 +1814,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
driver->beginScene(true, true,
|
||||
video::SColor(255,128,128,128));
|
||||
drawMenuBackground(driver);
|
||||
drawMenuBackground(driver, menutextures);
|
||||
guienv->drawAll();
|
||||
driver->endScene();
|
||||
// On some computers framerate doesn't seem to be
|
||||
@ -1637,22 +1828,6 @@ int main(int argc, char *argv[])
|
||||
&g_menumgr, &menudata, g_gamecallback);
|
||||
menu->allowFocusRemoval(true);
|
||||
|
||||
// Clouds for the main menu
|
||||
bool cloud_menu_background = false;
|
||||
Clouds *clouds = NULL;
|
||||
if (g_settings->getBool("menu_clouds")) {
|
||||
cloud_menu_background = true;
|
||||
clouds = new Clouds(smgr->getRootSceneNode(),
|
||||
smgr, -1, rand(), 100);
|
||||
clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
|
||||
|
||||
// A camera to see the clouds
|
||||
scene::ICameraSceneNode* camera;
|
||||
camera = smgr->addCameraSceneNode(0,
|
||||
v3f(0,0,0), v3f(0, 60, 100));
|
||||
camera->setFarValue(10000);
|
||||
}
|
||||
|
||||
if(error_message != L"")
|
||||
{
|
||||
verbosestream<<"error_message = "
|
||||
@ -1668,6 +1843,16 @@ int main(int argc, char *argv[])
|
||||
// Time is in milliseconds, for clouds
|
||||
u32 lasttime = device->getTimer()->getTime();
|
||||
|
||||
MenuMusicFetcher soundfetcher;
|
||||
ISoundManager *sound = NULL;
|
||||
sound = createOpenALSoundManager(&soundfetcher);
|
||||
if(!sound)
|
||||
sound = &dummySoundManager;
|
||||
SimpleSoundSpec spec;
|
||||
spec.name = "main_menu";
|
||||
spec.gain = 1;
|
||||
s32 handle = sound->playSound(spec, true);
|
||||
|
||||
infostream<<"Created main menu"<<std::endl;
|
||||
|
||||
while(device->run() && kill == false)
|
||||
@ -1675,8 +1860,25 @@ int main(int argc, char *argv[])
|
||||
if(menu->getStatus() == true)
|
||||
break;
|
||||
|
||||
// Game can be selected in the menu
|
||||
menugame = getMenuGame(menudata);
|
||||
menutextures.update(driver, menugame, menu->getTab());
|
||||
// Clouds for the main menu
|
||||
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||
if(menugame){
|
||||
// If game has regular background and no overlay, don't use clouds
|
||||
if(cloud_menu_background && menutextures.background &&
|
||||
!menutextures.overlay){
|
||||
cloud_menu_background = false;
|
||||
}
|
||||
// If game game has overlay and no regular background, always draw clouds
|
||||
else if(menutextures.overlay && !menutextures.background){
|
||||
cloud_menu_background = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Time calc for the clouds
|
||||
f32 dtime; // in seconds
|
||||
f32 dtime=0; // in seconds
|
||||
if (cloud_menu_background) {
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
@ -1691,15 +1893,16 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (cloud_menu_background) {
|
||||
// *3 otherwise the clouds would move very slowly
|
||||
clouds->step(dtime*3);
|
||||
clouds->render();
|
||||
smgr->drawAll();
|
||||
drawMenuSplash(driver);
|
||||
drawMenuFooter(driver, true);
|
||||
drawMenuHeader(driver);
|
||||
g_menuclouds->step(dtime*3);
|
||||
g_menuclouds->render();
|
||||
g_menucloudsmgr->drawAll();
|
||||
drawMenuOverlay(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
} else {
|
||||
drawMenuBackground(driver);
|
||||
drawMenuFooter(driver, false);
|
||||
drawMenuBackground(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
}
|
||||
|
||||
guienv->drawAll();
|
||||
@ -1731,14 +1934,15 @@ int main(int argc, char *argv[])
|
||||
sleep_ms(25);
|
||||
}
|
||||
}
|
||||
sound->stopSound(handle);
|
||||
if(sound != &dummySoundManager){
|
||||
delete sound;
|
||||
sound = NULL;
|
||||
}
|
||||
|
||||
infostream<<"Dropping main menu"<<std::endl;
|
||||
|
||||
menu->drop();
|
||||
if (cloud_menu_background) {
|
||||
clouds->drop();
|
||||
smgr->clear();
|
||||
}
|
||||
}
|
||||
|
||||
playername = wide_to_narrow(menudata.name);
|
||||
@ -1755,6 +1959,7 @@ int main(int argc, char *argv[])
|
||||
// Save settings
|
||||
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
|
||||
g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
|
||||
g_settings->set("selected_mainmenu_game", menudata.selected_game);
|
||||
g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
|
||||
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
|
||||
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
|
||||
@ -1832,6 +2037,7 @@ int main(int argc, char *argv[])
|
||||
continue;
|
||||
}
|
||||
g_settings->set("selected_world_path", path);
|
||||
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1897,6 +2103,7 @@ int main(int argc, char *argv[])
|
||||
gamespec,
|
||||
simple_singleplayer_mode
|
||||
);
|
||||
smgr->clear();
|
||||
|
||||
} //try
|
||||
catch(con::PeerNotFoundException &e)
|
||||
@ -1927,6 +2134,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
} // Menu-game loop
|
||||
|
||||
|
||||
g_menuclouds->drop();
|
||||
g_menucloudsmgr->drop();
|
||||
|
||||
delete input;
|
||||
|
||||
/*
|
||||
@ -1934,6 +2145,8 @@ int main(int argc, char *argv[])
|
||||
*/
|
||||
device->drop();
|
||||
|
||||
delete font;
|
||||
|
||||
#endif // !SERVER
|
||||
|
||||
// Update configuration file
|
||||
|
@ -28,6 +28,14 @@ extern Settings *g_settings;
|
||||
class Profiler;
|
||||
extern Profiler *g_profiler;
|
||||
|
||||
// Menu clouds
|
||||
class Clouds;
|
||||
extern Clouds *g_menuclouds;
|
||||
|
||||
// Scene manager used for menu clouds
|
||||
namespace irr{namespace scene{class ISceneManager;}}
|
||||
extern irr::scene::ISceneManager *g_menucloudsmgr;
|
||||
|
||||
// Debug streams
|
||||
|
||||
#include <fstream>
|
||||
|
@ -1338,12 +1338,20 @@ void MeshCollector::append(const TileSpec &tile,
|
||||
const video::S3DVertex *vertices, u32 numVertices,
|
||||
const u16 *indices, u32 numIndices)
|
||||
{
|
||||
if(numIndices > 65535)
|
||||
{
|
||||
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
PreMeshBuffer *p = NULL;
|
||||
for(u32 i=0; i<prebuffers.size(); i++)
|
||||
{
|
||||
PreMeshBuffer &pp = prebuffers[i];
|
||||
if(pp.tile != tile)
|
||||
continue;
|
||||
if(pp.indices.size() + numIndices > 65535)
|
||||
continue;
|
||||
|
||||
p = &pp;
|
||||
break;
|
||||
@ -1361,11 +1369,6 @@ void MeshCollector::append(const TileSpec &tile,
|
||||
for(u32 i=0; i<numIndices; i++)
|
||||
{
|
||||
u32 j = indices[i] + vertex_count;
|
||||
if(j > 65535)
|
||||
{
|
||||
dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
|
||||
// NOTE: Fix is to just add an another MeshBuffer
|
||||
}
|
||||
p->indices.push_back(j);
|
||||
}
|
||||
for(u32 i=0; i<numVertices; i++)
|
||||
|
@ -284,7 +284,6 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
|
||||
if (img2 != NULL)
|
||||
{
|
||||
img1->copyTo(img2);
|
||||
img1->drop();
|
||||
|
||||
mesh = extrudeARGB(size.Width, size.Height, (u8*) img2->lock());
|
||||
img2->unlock();
|
||||
|
371
src/mods.cpp
371
src/mods.cpp
@ -24,9 +24,74 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "subgame.h"
|
||||
#include "settings.h"
|
||||
#include "strfnd.h"
|
||||
#include <cctype>
|
||||
|
||||
std::map<std::string, ModSpec> getModsInPath(std::string path)
|
||||
static bool parseDependsLine(std::istream &is,
|
||||
std::string &dep, std::set<char> &symbols)
|
||||
{
|
||||
std::getline(is, dep);
|
||||
dep = trim(dep);
|
||||
symbols.clear();
|
||||
size_t pos = dep.size();
|
||||
while(pos > 0 && !string_allowed(dep.substr(pos-1, 1), MODNAME_ALLOWED_CHARS)){
|
||||
// last character is a symbol, not part of the modname
|
||||
symbols.insert(dep[pos-1]);
|
||||
--pos;
|
||||
}
|
||||
dep = trim(dep.substr(0, pos));
|
||||
return dep != "";
|
||||
}
|
||||
|
||||
void parseModContents(ModSpec &spec)
|
||||
{
|
||||
// NOTE: this function works in mutual recursion with getModsInPath
|
||||
|
||||
spec.depends.clear();
|
||||
spec.optdepends.clear();
|
||||
spec.is_modpack = false;
|
||||
spec.modpack_content.clear();
|
||||
|
||||
// Handle modpacks (defined by containing modpack.txt)
|
||||
std::ifstream modpack_is((spec.path+DIR_DELIM+"modpack.txt").c_str());
|
||||
if(modpack_is.good()){ //a modpack, recursively get the mods in it
|
||||
modpack_is.close(); // We don't actually need the file
|
||||
spec.is_modpack = true;
|
||||
spec.modpack_content = getModsInPath(spec.path, true);
|
||||
|
||||
// modpacks have no dependencies; they are defined and
|
||||
// tracked separately for each mod in the modpack
|
||||
}
|
||||
else{ // not a modpack, parse the dependencies
|
||||
std::ifstream is((spec.path+DIR_DELIM+"depends.txt").c_str());
|
||||
while(is.good()){
|
||||
std::string dep;
|
||||
std::set<char> symbols;
|
||||
if(parseDependsLine(is, dep, symbols)){
|
||||
if(symbols.count('?') != 0){
|
||||
spec.optdepends.insert(dep);
|
||||
}
|
||||
else{
|
||||
spec.depends.insert(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: optdepends.txt is deprecated
|
||||
// remove this code at some point in the future
|
||||
std::ifstream is2((spec.path+DIR_DELIM+"optdepends.txt").c_str());
|
||||
while(is2.good()){
|
||||
std::string dep;
|
||||
std::set<char> symbols;
|
||||
if(parseDependsLine(is2, dep, symbols))
|
||||
spec.optdepends.insert(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, ModSpec> getModsInPath(std::string path, bool part_of_modpack)
|
||||
{
|
||||
// NOTE: this function works in mutual recursion with parseModContents
|
||||
|
||||
std::map<std::string, ModSpec> result;
|
||||
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
|
||||
for(u32 j=0; j<dirlist.size(); j++){
|
||||
@ -39,38 +104,34 @@ std::map<std::string, ModSpec> getModsInPath(std::string path)
|
||||
continue;
|
||||
std::string modpath = path + DIR_DELIM + modname;
|
||||
|
||||
// Handle modpacks (defined by containing modpack.txt)
|
||||
std::ifstream modpack_is((modpath+DIR_DELIM+"modpack.txt").c_str(),
|
||||
std::ios_base::binary);
|
||||
if(modpack_is.good()) //a modpack, recursively get the mods in it
|
||||
{
|
||||
modpack_is.close(); // We don't actually need the file
|
||||
ModSpec spec(modname, modpath);
|
||||
spec.modpack_content = getModsInPath(modpath);
|
||||
spec.is_modpack = true;
|
||||
spec.part_of_modpack = part_of_modpack;
|
||||
parseModContents(spec);
|
||||
result.insert(std::make_pair(modname, spec));
|
||||
}
|
||||
else // not a modpack, add the modspec
|
||||
{
|
||||
std::set<std::string> depends;
|
||||
std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
|
||||
std::ios_base::binary);
|
||||
while(is.good())
|
||||
{
|
||||
std::string dep;
|
||||
std::getline(is, dep);
|
||||
dep = trim(dep);
|
||||
if(dep != "")
|
||||
depends.insert(dep);
|
||||
}
|
||||
|
||||
ModSpec spec(modname, modpath, depends);
|
||||
result.insert(std::make_pair(modname,spec));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ModSpec findCommonMod(const std::string &modname)
|
||||
{
|
||||
// Try to find in {$user,$share}/games/common/$modname
|
||||
std::vector<std::string> find_paths;
|
||||
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
|
||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
|
||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||
for(u32 i=0; i<find_paths.size(); i++){
|
||||
const std::string &try_path = find_paths[i];
|
||||
if(fs::PathExists(try_path)){
|
||||
ModSpec spec(modname, try_path);
|
||||
parseModContents(spec);
|
||||
return spec;
|
||||
}
|
||||
}
|
||||
// Failed to find mod
|
||||
return ModSpec();
|
||||
}
|
||||
|
||||
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods)
|
||||
{
|
||||
std::map<std::string, ModSpec> result;
|
||||
@ -109,109 +170,18 @@ std::vector<ModSpec> flattenMods(std::map<std::string, ModSpec> mods)
|
||||
}
|
||||
else //not a modpack
|
||||
{
|
||||
// infostream << "inserting mod " << mod.name << std::endl;
|
||||
result.push_back(mod);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
|
||||
std::set<std::string> exclude_mod_names)
|
||||
{
|
||||
std::vector<ModSpec> result;
|
||||
for(std::vector<ModSpec>::iterator it = mods.begin();
|
||||
it != mods.end(); ++it)
|
||||
{
|
||||
ModSpec& mod = *it;
|
||||
if(exclude_mod_names.count(mod.name) == 0)
|
||||
result.push_back(mod);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ModConfiguration::addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods)
|
||||
{
|
||||
addMods(filterMods(flattenMods(getModsInPath(path)),exclude_mods));
|
||||
}
|
||||
|
||||
|
||||
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
|
||||
{
|
||||
// Step 1: remove mods in sorted_mods from unmet dependencies
|
||||
// of new_mods. new mods without unmet dependencies are
|
||||
// temporarily stored in satisfied_mods
|
||||
std::vector<ModSpec> satisfied_mods;
|
||||
for(std::vector<ModSpec>::iterator it = m_sorted_mods.begin();
|
||||
it != m_sorted_mods.end(); ++it)
|
||||
{
|
||||
ModSpec mod = *it;
|
||||
for(std::vector<ModSpec>::iterator it_new = new_mods.begin();
|
||||
it_new != new_mods.end(); ++it_new)
|
||||
{
|
||||
ModSpec& mod_new = *it_new;
|
||||
//infostream << "erasing dependency " << mod.name << " from " << mod_new.name << std::endl;
|
||||
mod_new.unsatisfied_depends.erase(mod.name);
|
||||
}
|
||||
}
|
||||
|
||||
// split new mods into satisfied and unsatisfied
|
||||
for(std::vector<ModSpec>::iterator it = new_mods.begin();
|
||||
it != new_mods.end(); ++it)
|
||||
{
|
||||
ModSpec mod_new = *it;
|
||||
if(mod_new.unsatisfied_depends.empty())
|
||||
satisfied_mods.push_back(mod_new);
|
||||
else
|
||||
m_unsatisfied_mods.push_back(mod_new);
|
||||
}
|
||||
|
||||
// Step 2: mods without unmet dependencies can be appended to
|
||||
// the sorted list.
|
||||
while(!satisfied_mods.empty())
|
||||
{
|
||||
ModSpec mod = satisfied_mods.back();
|
||||
m_sorted_mods.push_back(mod);
|
||||
satisfied_mods.pop_back();
|
||||
for(std::list<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
||||
it != m_unsatisfied_mods.end(); )
|
||||
{
|
||||
ModSpec& mod2 = *it;
|
||||
mod2.unsatisfied_depends.erase(mod.name);
|
||||
if(mod2.unsatisfied_depends.empty())
|
||||
{
|
||||
satisfied_mods.push_back(mod2);
|
||||
it = m_unsatisfied_mods.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If failed, returned modspec has name==""
|
||||
static ModSpec findCommonMod(const std::string &modname)
|
||||
{
|
||||
// Try to find in {$user,$share}/games/common/$modname
|
||||
std::vector<std::string> find_paths;
|
||||
find_paths.push_back(porting::path_user + DIR_DELIM + "games" +
|
||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||
find_paths.push_back(porting::path_share + DIR_DELIM + "games" +
|
||||
DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname);
|
||||
for(u32 i=0; i<find_paths.size(); i++){
|
||||
const std::string &try_path = find_paths[i];
|
||||
if(fs::PathExists(try_path))
|
||||
return ModSpec(modname, try_path);
|
||||
}
|
||||
// Failed to find mod
|
||||
return ModSpec();
|
||||
}
|
||||
|
||||
ModConfiguration::ModConfiguration(std::string worldpath)
|
||||
{
|
||||
SubgameSpec gamespec = findWorldSubgame(worldpath);
|
||||
|
||||
// Add common mods without dependency handling
|
||||
// Add common mods
|
||||
std::map<std::string, ModSpec> common_mods;
|
||||
std::vector<std::string> inexistent_common_mods;
|
||||
Settings gameconf;
|
||||
if(getGameConfig(gamespec.path, gameconf)){
|
||||
@ -225,7 +195,7 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
||||
if(spec.name.empty())
|
||||
inexistent_common_mods.push_back(modname);
|
||||
else
|
||||
m_sorted_mods.push_back(spec);
|
||||
common_mods.insert(std::make_pair(modname, spec));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,10 +208,11 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
||||
s += " could not be found.";
|
||||
throw ModError(s);
|
||||
}
|
||||
addMods(flattenMods(common_mods));
|
||||
|
||||
// Add all world mods and all game mods
|
||||
addModsInPath(worldpath + DIR_DELIM + "worldmods");
|
||||
// Add all game mods and all world mods
|
||||
addModsInPath(gamespec.gamemods_path);
|
||||
addModsInPath(worldpath + DIR_DELIM + "worldmods");
|
||||
|
||||
// check world.mt file for mods explicitely declared to be
|
||||
// loaded or not by a load_mod_<modname> = ... line.
|
||||
@ -264,7 +235,155 @@ ModConfiguration::ModConfiguration(std::string worldpath)
|
||||
}
|
||||
}
|
||||
|
||||
for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin();
|
||||
i != gamespec.addon_mods_paths.end(); ++i)
|
||||
addModsInPathFiltered((*i),exclude_mod_names);
|
||||
// Collect all mods in gamespec.addon_mods_paths,
|
||||
// excluding those in the set exclude_mod_names
|
||||
std::vector<ModSpec> addon_mods;
|
||||
for(std::set<std::string>::const_iterator it_path = gamespec.addon_mods_paths.begin();
|
||||
it_path != gamespec.addon_mods_paths.end(); ++it_path)
|
||||
{
|
||||
std::vector<ModSpec> addon_mods_in_path = flattenMods(getModsInPath(*it_path));
|
||||
for(std::vector<ModSpec>::iterator it = addon_mods_in_path.begin();
|
||||
it != addon_mods_in_path.end(); ++it)
|
||||
{
|
||||
ModSpec& mod = *it;
|
||||
if(exclude_mod_names.count(mod.name) == 0)
|
||||
addon_mods.push_back(mod);
|
||||
}
|
||||
}
|
||||
|
||||
addMods(addon_mods);
|
||||
|
||||
// report on name conflicts
|
||||
if(!m_name_conflicts.empty()){
|
||||
std::string s = "Unresolved name conflicts for mods ";
|
||||
for(std::set<std::string>::const_iterator it = m_name_conflicts.begin();
|
||||
it != m_name_conflicts.end(); ++it)
|
||||
{
|
||||
if(it != m_name_conflicts.begin()) s += ", ";
|
||||
s += std::string("\"") + (*it) + "\"";
|
||||
}
|
||||
s += ".";
|
||||
throw ModError(s);
|
||||
}
|
||||
|
||||
// get the mods in order
|
||||
resolveDependencies();
|
||||
}
|
||||
|
||||
void ModConfiguration::addModsInPath(std::string path)
|
||||
{
|
||||
addMods(flattenMods(getModsInPath(path)));
|
||||
}
|
||||
|
||||
void ModConfiguration::addMods(std::vector<ModSpec> new_mods)
|
||||
{
|
||||
// Maintain a map of all existing m_unsatisfied_mods.
|
||||
// Keys are mod names and values are indices into m_unsatisfied_mods.
|
||||
std::map<std::string, u32> existing_mods;
|
||||
for(u32 i = 0; i < m_unsatisfied_mods.size(); ++i){
|
||||
existing_mods[m_unsatisfied_mods[i].name] = i;
|
||||
}
|
||||
|
||||
// Add new mods
|
||||
for(int want_from_modpack = 1; want_from_modpack >= 0; --want_from_modpack){
|
||||
// First iteration:
|
||||
// Add all the mods that come from modpacks
|
||||
// Second iteration:
|
||||
// Add all the mods that didn't come from modpacks
|
||||
|
||||
std::set<std::string> seen_this_iteration;
|
||||
|
||||
for(std::vector<ModSpec>::const_iterator it = new_mods.begin();
|
||||
it != new_mods.end(); ++it){
|
||||
const ModSpec &mod = *it;
|
||||
if(mod.part_of_modpack != want_from_modpack)
|
||||
continue;
|
||||
if(existing_mods.count(mod.name) == 0){
|
||||
// GOOD CASE: completely new mod.
|
||||
m_unsatisfied_mods.push_back(mod);
|
||||
existing_mods[mod.name] = m_unsatisfied_mods.size() - 1;
|
||||
}
|
||||
else if(seen_this_iteration.count(mod.name) == 0){
|
||||
// BAD CASE: name conflict in different levels.
|
||||
u32 oldindex = existing_mods[mod.name];
|
||||
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
|
||||
errorstream<<"WARNING: Mod name conflict detected: \""
|
||||
<<mod.name<<"\""<<std::endl
|
||||
<<"Will not load: "<<oldmod.path<<std::endl
|
||||
<<"Overridden by: "<<mod.path<<std::endl;
|
||||
m_unsatisfied_mods[oldindex] = mod;
|
||||
|
||||
// If there was a "VERY BAD CASE" name conflict
|
||||
// in an earlier level, ignore it.
|
||||
m_name_conflicts.erase(mod.name);
|
||||
}
|
||||
else{
|
||||
// VERY BAD CASE: name conflict in the same level.
|
||||
u32 oldindex = existing_mods[mod.name];
|
||||
const ModSpec &oldmod = m_unsatisfied_mods[oldindex];
|
||||
errorstream<<"WARNING: Mod name conflict detected: \""
|
||||
<<mod.name<<"\""<<std::endl
|
||||
<<"Will not load: "<<oldmod.path<<std::endl
|
||||
<<"Will not load: "<<mod.path<<std::endl;
|
||||
m_unsatisfied_mods[oldindex] = mod;
|
||||
m_name_conflicts.insert(mod.name);
|
||||
}
|
||||
seen_this_iteration.insert(mod.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModConfiguration::resolveDependencies()
|
||||
{
|
||||
// Step 1: Compile a list of the mod names we're working with
|
||||
std::set<std::string> modnames;
|
||||
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
||||
it != m_unsatisfied_mods.end(); ++it){
|
||||
modnames.insert((*it).name);
|
||||
}
|
||||
|
||||
// Step 2: get dependencies (including optional dependencies)
|
||||
// of each mod, split mods into satisfied and unsatisfied
|
||||
std::list<ModSpec> satisfied;
|
||||
std::list<ModSpec> unsatisfied;
|
||||
for(std::vector<ModSpec>::iterator it = m_unsatisfied_mods.begin();
|
||||
it != m_unsatisfied_mods.end(); ++it){
|
||||
ModSpec mod = *it;
|
||||
mod.unsatisfied_depends = mod.depends;
|
||||
// check which optional dependencies actually exist
|
||||
for(std::set<std::string>::iterator it_optdep = mod.optdepends.begin();
|
||||
it_optdep != mod.optdepends.end(); ++it_optdep){
|
||||
std::string optdep = *it_optdep;
|
||||
if(modnames.count(optdep) != 0)
|
||||
mod.unsatisfied_depends.insert(optdep);
|
||||
}
|
||||
// if a mod has no depends it is initially satisfied
|
||||
if(mod.unsatisfied_depends.empty())
|
||||
satisfied.push_back(mod);
|
||||
else
|
||||
unsatisfied.push_back(mod);
|
||||
}
|
||||
|
||||
// Step 3: mods without unmet dependencies can be appended to
|
||||
// the sorted list.
|
||||
while(!satisfied.empty()){
|
||||
ModSpec mod = satisfied.back();
|
||||
m_sorted_mods.push_back(mod);
|
||||
satisfied.pop_back();
|
||||
for(std::list<ModSpec>::iterator it = unsatisfied.begin();
|
||||
it != unsatisfied.end(); ){
|
||||
ModSpec& mod2 = *it;
|
||||
mod2.unsatisfied_depends.erase(mod.name);
|
||||
if(mod2.unsatisfied_depends.empty()){
|
||||
satisfied.push_back(mod2);
|
||||
it = unsatisfied.erase(it);
|
||||
}
|
||||
else{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: write back list of unsatisfied mods
|
||||
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
|
||||
}
|
||||
|
72
src/mods.h
72
src/mods.h
@ -30,6 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <exception>
|
||||
#include <list>
|
||||
|
||||
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
|
||||
class ModError : public std::exception
|
||||
{
|
||||
public:
|
||||
@ -53,23 +55,32 @@ struct ModSpec
|
||||
std::string path;
|
||||
//if normal mod:
|
||||
std::set<std::string> depends;
|
||||
std::set<std::string> optdepends;
|
||||
std::set<std::string> unsatisfied_depends;
|
||||
|
||||
bool part_of_modpack;
|
||||
bool is_modpack;
|
||||
// if modpack:
|
||||
std::map<std::string,ModSpec> modpack_content;
|
||||
ModSpec(const std::string name_="", const std::string path_="",
|
||||
const std::set<std::string> depends_=std::set<std::string>()):
|
||||
ModSpec(const std::string name_="", const std::string path_=""):
|
||||
name(name_),
|
||||
path(path_),
|
||||
depends(depends_),
|
||||
unsatisfied_depends(depends_),
|
||||
depends(),
|
||||
optdepends(),
|
||||
unsatisfied_depends(),
|
||||
part_of_modpack(false),
|
||||
is_modpack(false),
|
||||
modpack_content()
|
||||
{}
|
||||
};
|
||||
|
||||
std::map<std::string,ModSpec> getModsInPath(std::string path);
|
||||
// Retrieves depends, optdepends, is_modpack and modpack_content
|
||||
void parseModContents(ModSpec &mod);
|
||||
|
||||
std::map<std::string,ModSpec> getModsInPath(std::string path, bool part_of_modpack = false);
|
||||
|
||||
// If failed, returned modspec has name==""
|
||||
ModSpec findCommonMod(const std::string &modname);
|
||||
|
||||
// expands modpack contents, but does not replace them.
|
||||
std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mods);
|
||||
@ -77,10 +88,6 @@ std::map<std::string, ModSpec> flattenModTree(std::map<std::string, ModSpec> mod
|
||||
// replaces modpack Modspecs with their content
|
||||
std::vector<ModSpec> flattenMods(std::map<std::string,ModSpec> mods);
|
||||
|
||||
// removes Mods mentioned in exclude_mod_names
|
||||
std::vector<ModSpec> filterMods(std::vector<ModSpec> mods,
|
||||
std::set<std::string> exclude_mod_names);
|
||||
|
||||
// a ModConfiguration is a subset of installed mods, expected to have
|
||||
// all dependencies fullfilled, so it can be used as a list of mods to
|
||||
// load when the game starts.
|
||||
@ -89,26 +96,13 @@ class ModConfiguration
|
||||
public:
|
||||
ModConfiguration():
|
||||
m_unsatisfied_mods(),
|
||||
m_sorted_mods()
|
||||
m_sorted_mods(),
|
||||
m_name_conflicts()
|
||||
{}
|
||||
|
||||
|
||||
ModConfiguration(std::string worldpath);
|
||||
|
||||
// adds all mods in the given path. used for games, modpacks
|
||||
// and world-specific mods (worldmods-folders)
|
||||
void addModsInPath(std::string path)
|
||||
{
|
||||
addMods(flattenMods(getModsInPath(path)));
|
||||
}
|
||||
|
||||
// adds all mods in the given path whose name does not appear
|
||||
// in the exclude_mods set.
|
||||
void addModsInPathFiltered(std::string path, std::set<std::string> exclude_mods);
|
||||
|
||||
// adds all mods in the set.
|
||||
void addMods(std::vector<ModSpec> mods);
|
||||
|
||||
// checks if all dependencies are fullfilled.
|
||||
bool isConsistent()
|
||||
{
|
||||
@ -120,17 +114,27 @@ public:
|
||||
return m_sorted_mods;
|
||||
}
|
||||
|
||||
std::list<ModSpec> getUnsatisfiedMods()
|
||||
std::vector<ModSpec> getUnsatisfiedMods()
|
||||
{
|
||||
return m_unsatisfied_mods;
|
||||
}
|
||||
|
||||
private:
|
||||
// adds all mods in the given path. used for games, modpacks
|
||||
// and world-specific mods (worldmods-folders)
|
||||
void addModsInPath(std::string path);
|
||||
|
||||
// mods with unmet dependencies. This is a list and not a
|
||||
// vector because we want easy removal of elements at every
|
||||
// position.
|
||||
std::list<ModSpec> m_unsatisfied_mods;
|
||||
// adds all mods in the set.
|
||||
void addMods(std::vector<ModSpec> new_mods);
|
||||
|
||||
// move mods from m_unsatisfied_mods to m_sorted_mods
|
||||
// in an order that satisfies dependencies
|
||||
void resolveDependencies();
|
||||
|
||||
// mods with unmet dependencies. Before dependencies are resolved,
|
||||
// this is where all mods are stored. Afterwards this contains
|
||||
// only the ones with really unsatisfied dependencies.
|
||||
std::vector<ModSpec> m_unsatisfied_mods;
|
||||
|
||||
// list of mods sorted such that they can be loaded in the
|
||||
// given order with all dependencies being fullfilled. I.e.,
|
||||
@ -138,6 +142,16 @@ private:
|
||||
// appear earlier in the vector.
|
||||
std::vector<ModSpec> m_sorted_mods;
|
||||
|
||||
// set of mod names for which an unresolved name conflict
|
||||
// exists. A name conflict happens when two or more mods
|
||||
// at the same level have the same name but different paths.
|
||||
// Levels (mods in higher levels override mods in lower levels):
|
||||
// 1. common mod in modpack; 2. common mod;
|
||||
// 3. game mod in modpack; 4. game mod;
|
||||
// 5. world mod in modpack; 6. world mod;
|
||||
// 7. addon mod in modpack; 8. addon mod.
|
||||
std::set<std::string> m_name_conflicts;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -76,8 +76,7 @@ bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
|
||||
{
|
||||
ModNameStorer modnamestorer(L, modname);
|
||||
|
||||
if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789_")){
|
||||
if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
|
||||
errorstream<<"Error loading mod \""<<modname
|
||||
<<"\": modname does not follow naming conventions: "
|
||||
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
|
||||
|
@ -707,11 +707,11 @@ Server::Server(
|
||||
|
||||
ModConfiguration modconf(m_path_world);
|
||||
m_mods = modconf.getMods();
|
||||
std::list<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
|
||||
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
|
||||
// complain about mods with unsatisfied dependencies
|
||||
if(!modconf.isConsistent())
|
||||
{
|
||||
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||
it != unsatisfied_mods.end(); ++it)
|
||||
{
|
||||
ModSpec mod = *it;
|
||||
@ -745,7 +745,7 @@ Server::Server(
|
||||
for(std::vector<ModSpec>::iterator it = m_mods.begin();
|
||||
it != m_mods.end(); ++it)
|
||||
load_mod_names.erase((*it).name);
|
||||
for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
|
||||
it != unsatisfied_mods.end(); ++it)
|
||||
load_mod_names.erase((*it).name);
|
||||
if(!load_mod_names.empty())
|
||||
@ -2980,12 +2980,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
||||
playersao->setWieldedItem(item);
|
||||
}
|
||||
|
||||
// If item has node placement prediction, always send the above
|
||||
// node to make sure the client knows what exactly happened
|
||||
// If item has node placement prediction, always send the
|
||||
// blocks to make sure the client knows what exactly happened
|
||||
if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
|
||||
RemoteClient *client = getClient(peer_id);
|
||||
v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
|
||||
client->SetBlockNotSent(blockpos);
|
||||
v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
|
||||
if(blockpos2 != blockpos){
|
||||
client->SetBlockNotSent(blockpos2);
|
||||
}
|
||||
}
|
||||
} // action == 3
|
||||
|
||||
@ -5073,6 +5077,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
|
||||
getPlayerEffectivePrivs(player->getName()),
|
||||
isSingleplayer());
|
||||
|
||||
/* Clean up old HUD elements from previous sessions */
|
||||
player->hud.clear();
|
||||
|
||||
/* Add object to environment */
|
||||
m_env->addActiveObject(playersao);
|
||||
|
||||
|
@ -53,7 +53,7 @@ std::vector<ServerListSpec> getLocal()
|
||||
std::string liststring;
|
||||
if(fs::PathExists(path))
|
||||
{
|
||||
std::ifstream istream(path.c_str(), std::ios::binary);
|
||||
std::ifstream istream(path.c_str());
|
||||
if(istream.is_open())
|
||||
{
|
||||
std::ostringstream ostream;
|
||||
|
@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
||||
|
||||
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 2 of the License, or
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 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.
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
@ -4,16 +4,16 @@ Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
Copyright (C) 2013 Kahrl <kahrl@gmx.net>
|
||||
|
||||
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 2 of the License, or
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 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.
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
@ -22,6 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "filesys.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#ifndef SERVER
|
||||
#include "tile.h" // getImagePath
|
||||
#endif
|
||||
#include "util/string.h"
|
||||
|
||||
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
|
||||
@ -94,7 +97,12 @@ SubgameSpec findSubgame(const std::string &id)
|
||||
std::string game_name = getGameName(game_path);
|
||||
if(game_name == "")
|
||||
game_name = id;
|
||||
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name);
|
||||
std::string menuicon_path;
|
||||
#ifndef SERVER
|
||||
menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
|
||||
#endif
|
||||
return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name,
|
||||
menuicon_path);
|
||||
}
|
||||
|
||||
SubgameSpec findWorldSubgame(const std::string &world_path)
|
||||
|
@ -35,17 +35,20 @@ struct SubgameSpec
|
||||
std::string gamemods_path; //path to mods of the game
|
||||
std::set<std::string> addon_mods_paths; //paths to addon mods for this game
|
||||
std::string name;
|
||||
std::string menuicon_path;
|
||||
|
||||
SubgameSpec(const std::string &id_="",
|
||||
const std::string &path_="",
|
||||
const std::string &gamemods_path_="",
|
||||
const std::set<std::string> &addon_mods_paths_=std::set<std::string>(),
|
||||
const std::string &name_=""):
|
||||
const std::string &name_="",
|
||||
const std::string &menuicon_path_=""):
|
||||
id(id_),
|
||||
path(path_),
|
||||
gamemods_path(gamemods_path_),
|
||||
addon_mods_paths(addon_mods_paths_),
|
||||
name(name_)
|
||||
name(name_),
|
||||
menuicon_path(menuicon_path_)
|
||||
{}
|
||||
|
||||
bool isValid() const
|
||||
|
@ -77,7 +77,7 @@ static bool replace_ext(std::string &path, const char *ext)
|
||||
|
||||
If failed, return "".
|
||||
*/
|
||||
static std::string getImagePath(std::string path)
|
||||
std::string getImagePath(std::string path)
|
||||
{
|
||||
// A NULL-ended list of possible image extensions
|
||||
const char *extensions[] = {
|
||||
|
11
src/tile.h
11
src/tile.h
@ -34,6 +34,17 @@ class IGameDef;
|
||||
tile.{h,cpp}: Texture handling stuff.
|
||||
*/
|
||||
|
||||
/*
|
||||
Find out the full path of an image by trying different filename
|
||||
extensions.
|
||||
|
||||
If failed, return "".
|
||||
|
||||
TODO: Should probably be moved out from here, because things needing
|
||||
this function do not need anything else from this header
|
||||
*/
|
||||
std::string getImagePath(std::string path);
|
||||
|
||||
/*
|
||||
Gets the path to a texture by first checking if the texture exists
|
||||
in texture_path and if not, using the data path.
|
||||
|
Loading…
Reference in New Issue
Block a user