#include "gameparams.h"
#include "gettext.h"
#include "gui/guiChatConsole.h"
-#include "gui/guiConfirmRegistration.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/guiKeyChangeMenu.h"
#include "gui/guiPasswordChange.h"
#include "version.h"
#include "script/scripting_client.h"
#include "hud.h"
+#include "clientdynamicinfo.h"
#if USE_SOUND
#include "client/sound_openal.h"
TextDestPlayerInventory(Client *client)
{
m_client = client;
- m_formname = "";
+ m_formname.clear();
}
TextDestPlayerInventory(Client *client, const std::string &formname)
{
Client *m_client;
};
-class NodeDugEvent: public MtEvent
+class NodeDugEvent : public MtEvent
{
public:
v3s16 p;
p(p),
n(n)
{}
- MtEvent::Type getType() const
- {
- return MtEvent::NODE_DUG;
- }
+ Type getType() const { return NODE_DUG; }
};
class SoundMaker
{
ISoundManager *m_sound;
const NodeDefManager *m_ndef;
+
public:
bool makes_footstep_sound;
float m_player_step_timer;
SimpleSoundSpec m_player_step_sound;
SimpleSoundSpec m_player_leftpunch_sound;
+ // Second sound made on left punch, currently used for item 'use' sound
+ SimpleSoundSpec m_player_leftpunch_sound2;
SimpleSoundSpec m_player_rightpunch_sound;
SoundMaker(ISoundManager *sound, const NodeDefManager *ndef):
if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
m_player_step_timer = 0.03;
if (makes_footstep_sound)
- m_sound->playSound(m_player_step_sound, false);
+ m_sound->playSound(m_player_step_sound);
}
}
{
if (m_player_jump_timer <= 0.0f) {
m_player_jump_timer = 0.2f;
- m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
+ m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f));
}
}
static void cameraPunchLeft(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
- sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
+ sm->m_sound->playSound(sm->m_player_leftpunch_sound);
+ sm->m_sound->playSound(sm->m_player_leftpunch_sound2);
}
static void cameraPunchRight(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
- sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
+ sm->m_sound->playSound(sm->m_player_rightpunch_sound);
}
static void nodeDug(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
NodeDugEvent *nde = (NodeDugEvent *)e;
- sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
+ sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug);
}
static void playerDamage(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
- sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
+ sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5));
}
static void playerFallingDamage(MtEvent *e, void *data)
{
SoundMaker *sm = (SoundMaker *)data;
- sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
+ sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5));
}
void registerReceiver(MtEventManager *mgr)
class GameGlobalShaderConstantSetter : public IShaderConstantSetter
{
Sky *m_sky;
+ Client *m_client;
bool *m_force_fog_off;
f32 *m_fog_range;
bool m_fog_enabled;
CachedPixelShaderSetting<float> m_fog_distance;
CachedVertexShaderSetting<float> m_animation_timer_vertex;
CachedPixelShaderSetting<float> m_animation_timer_pixel;
+ CachedVertexShaderSetting<float> m_animation_timer_delta_vertex;
+ CachedPixelShaderSetting<float> m_animation_timer_delta_pixel;
CachedPixelShaderSetting<float, 3> m_day_light;
CachedPixelShaderSetting<float, 4> m_star_color;
CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
CachedPixelShaderSetting<float, 3> m_minimap_yaw;
CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
- CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
- CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture;
- Client *m_client;
+ CachedPixelShaderSetting<SamplerLayer_t> m_texture0;
+ CachedPixelShaderSetting<SamplerLayer_t> m_texture1;
+ CachedPixelShaderSetting<SamplerLayer_t> m_texture2;
+ CachedPixelShaderSetting<SamplerLayer_t> m_texture3;
+ CachedPixelShaderSetting<float, 2> m_texel_size0;
+ std::array<float, 2> m_texel_size0_values;
+ CachedStructPixelShaderSetting<float, 7> m_exposure_params_pixel;
+ float m_user_exposure_compensation;
+ bool m_bloom_enabled;
+ CachedPixelShaderSetting<float> m_bloom_intensity_pixel;
+ float m_bloom_intensity;
+ CachedPixelShaderSetting<float> m_bloom_strength_pixel;
+ float m_bloom_strength;
+ CachedPixelShaderSetting<float> m_bloom_radius_pixel;
+ float m_bloom_radius;
+ CachedPixelShaderSetting<float> m_saturation_pixel;
public:
void onSettingsChange(const std::string &name)
{
if (name == "enable_fog")
m_fog_enabled = g_settings->getBool("enable_fog");
+ if (name == "exposure_compensation")
+ m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
+ if (name == "bloom_intensity")
+ m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
+ if (name == "bloom_strength_factor")
+ m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
+ if (name == "bloom_radius")
+ m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
}
static void settingsCallback(const std::string &name, void *userdata)
GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
f32 *fog_range, Client *client) :
m_sky(sky),
+ m_client(client),
m_force_fog_off(force_fog_off),
m_fog_range(fog_range),
m_sky_bg_color("skyBgColor"),
m_fog_distance("fogDistance"),
m_animation_timer_vertex("animationTimer"),
m_animation_timer_pixel("animationTimer"),
+ m_animation_timer_delta_vertex("animationTimerDelta"),
+ m_animation_timer_delta_pixel("animationTimerDelta"),
m_day_light("dayLight"),
m_star_color("starColor"),
m_eye_position_pixel("eyePosition"),
m_minimap_yaw("yawVec"),
m_camera_offset_pixel("cameraOffset"),
m_camera_offset_vertex("cameraOffset"),
- m_base_texture("baseTexture"),
- m_normal_texture("normalTexture"),
- m_client(client)
+ m_texture0("texture0"),
+ m_texture1("texture1"),
+ m_texture2("texture2"),
+ m_texture3("texture3"),
+ m_texel_size0("texelSize0"),
+ m_exposure_params_pixel("exposureParams",
+ std::array<const char*, 7> {
+ "luminanceMin", "luminanceMax", "exposureCorrection",
+ "speedDarkBright", "speedBrightDark", "centerWeightPower", "compensationFactor"
+ }),
+ m_bloom_intensity_pixel("bloomIntensity"),
+ m_bloom_strength_pixel("bloomStrength"),
+ m_bloom_radius_pixel("bloomRadius"),
+ m_saturation_pixel("saturation")
{
g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
+ g_settings->registerChangedCallback("exposure_compensation", settingsCallback, this);
+ g_settings->registerChangedCallback("bloom_intensity", settingsCallback, this);
+ g_settings->registerChangedCallback("bloom_strength_factor", settingsCallback, this);
+ g_settings->registerChangedCallback("bloom_radius", settingsCallback, this);
+ g_settings->registerChangedCallback("saturation", settingsCallback, this);
m_fog_enabled = g_settings->getBool("enable_fog");
+ m_user_exposure_compensation = g_settings->getFloat("exposure_compensation", -1.0f, 1.0f);
+ m_bloom_enabled = g_settings->getBool("enable_bloom");
+ m_bloom_intensity = g_settings->getFloat("bloom_intensity", 0.01f, 1.0f);
+ m_bloom_strength = RenderingEngine::BASE_BLOOM_STRENGTH * g_settings->getFloat("bloom_strength_factor", 0.1f, 10.0f);
+ m_bloom_radius = g_settings->getFloat("bloom_radius", 0.1f, 8.0f);
}
~GameGlobalShaderConstantSetter()
float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
m_star_color.set(clr, services);
- u32 animation_timer = porting::getTimeMs() % 1000000;
+ u32 animation_timer = m_client->getEnv().getFrameTime() % 1000000;
float animation_timer_f = (float)animation_timer / 100000.f;
m_animation_timer_vertex.set(&animation_timer_f, services);
m_animation_timer_pixel.set(&animation_timer_f, services);
+ float animation_timer_delta_f = (float)m_client->getEnv().getFrameTimeDelta() / 100000.f;
+ m_animation_timer_delta_vertex.set(&animation_timer_delta_f, services);
+ m_animation_timer_delta_pixel.set(&animation_timer_delta_f, services);
+
float eye_position_array[3];
v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
epos.getAs3Values(eye_position_array);
m_camera_offset_pixel.set(camera_offset_array, services);
m_camera_offset_vertex.set(camera_offset_array, services);
- SamplerLayer_t base_tex = 0, normal_tex = 1;
- m_base_texture.set(&base_tex, services);
- m_normal_texture.set(&normal_tex, services);
+ SamplerLayer_t tex_id;
+ tex_id = 0;
+ m_texture0.set(&tex_id, services);
+ tex_id = 1;
+ m_texture1.set(&tex_id, services);
+ tex_id = 2;
+ m_texture2.set(&tex_id, services);
+ tex_id = 3;
+ m_texture3.set(&tex_id, services);
+
+ m_texel_size0.set(m_texel_size0_values.data(), services);
+
+ const AutoExposure &exposure_params = m_client->getEnv().getLocalPlayer()->getLighting().exposure;
+ std::array<float, 7> exposure_buffer = {
+ std::pow(2.0f, exposure_params.luminance_min),
+ std::pow(2.0f, exposure_params.luminance_max),
+ exposure_params.exposure_correction,
+ exposure_params.speed_dark_bright,
+ exposure_params.speed_bright_dark,
+ exposure_params.center_weight_power,
+ powf(2.f, m_user_exposure_compensation)
+ };
+ m_exposure_params_pixel.set(exposure_buffer.data(), services);
+
+ if (m_bloom_enabled) {
+ m_bloom_intensity_pixel.set(&m_bloom_intensity, services);
+ m_bloom_radius_pixel.set(&m_bloom_radius, services);
+ m_bloom_strength_pixel.set(&m_bloom_strength, services);
+ }
+ float saturation = m_client->getEnv().getLocalPlayer()->getLighting().saturation;
+ m_saturation_pixel.set(&saturation, services);
+ }
+
+ void onSetMaterial(const video::SMaterial &material)
+ {
+ video::ITexture *texture = material.getTexture(0);
+ if (texture) {
+ core::dimension2du size = texture->getSize();
+ m_texel_size0_values[0] = 1.f / size.Width;
+ m_texel_size0_values[1] = 1.f / size.Height;
+ }
+ else {
+ m_texel_size0_values[0] = 0.f;
+ m_texel_size0_values[1] = 0.f;
+ }
}
};
}
};
-#ifdef __ANDROID__
+#ifdef HAVE_TOUCHSCREENGUI
#define SIZE_TAG "size[11,5.5]"
#else
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
/****************************************************************************
****************************************************************************/
-const float object_hit_delay = 0.2;
+const static float object_hit_delay = 0.2;
struct FpsControl {
- u32 last_time, busy_time, sleep_time;
+ FpsControl() : last_time(0), busy_time(0), sleep_time(0) {}
+
+ void reset();
+
+ void limit(IrrlichtDevice *device, f32 *dtime);
+
+ u32 getBusyMs() const { return busy_time / 1000; }
+
+ // all values in microseconds (us)
+ u64 last_time, busy_time, sleep_time;
};
float time_from_last_punch;
ClientActiveObject *selected_object;
- float jump_timer;
+ float jump_timer_up; // from key up until key down
+ float jump_timer_down; // since last key down
+ float jump_timer_down_before; // from key down until key down again
+
float damage_flash;
float update_draw_list_timer;
+ float touch_blocks_timer;
f32 fog_range;
void updateCameraDirection(CameraOrientation *cam, float dtime);
void updateCameraOrientation(CameraOrientation *cam, float dtime);
void updatePlayerControl(const CameraOrientation &cam);
- void step(f32 *dtime);
+ void step(f32 dtime);
void processClientEvents(CameraOrientation *cam);
- void updateCamera(u32 busy_time, f32 dtime);
+ void updateCamera(f32 dtime);
void updateSound(f32 dtime);
- void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug);
+ void processPlayerInteraction(f32 dtime, bool show_hud);
/*!
* Returns the object or node the player is pointing at.
* Also updates the selected thing in the Hud.
void updateShadows();
// Misc
- void limitFps(FpsControl *fps_timings, f32 *dtime);
-
void showOverlayMessage(const char *msg, float dtime, int percent,
bool draw_clouds = true);
void updateChat(f32 dtime);
bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
- const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed,
+ const v3s16 &nodepos, const v3s16 &neighborpos, const PointedThing &pointed,
const NodeMetadata *meta);
static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
f32 getSensitivityScaleFactor() const;
+ ClientDynamicInfo getCurrentDynamicInfo() const;
InputHandler *input = nullptr;
Client *client = nullptr;
Server *server = nullptr;
+ ClientDynamicInfo client_display_info{};
+ float dynamic_info_send_timer = 0;
+
IWritableTextureSource *texture_src = nullptr;
IWritableShaderSource *shader_src = nullptr;
EventManager *eventmgr = nullptr;
QuicktuneShortcutter *quicktune = nullptr;
- bool registration_confirmation_shown = false;
std::unique_ptr<GameUI> m_game_ui;
GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
bool m_does_lost_focus_pause_game = false;
- int m_reset_HW_buffer_counter = 0;
-#ifdef __ANDROID__
+ // if true, (almost) the whole game is paused
+ // this happens in pause menu in singleplayer
+ bool m_is_paused = false;
+
+#ifdef HAVE_TOUCHSCREENGUI
bool m_cache_hold_aux1;
+ bool m_touch_use_crosshair;
+ inline bool isNoCrosshairAllowed() {
+ return !m_touch_use_crosshair && camera->getCameraMode() == CAMERA_MODE_FIRST;
+ }
+#endif
+#ifdef __ANDROID__
bool m_android_chat_open;
#endif
};
readSettings();
-#ifdef __ANDROID__
+#ifdef HAVE_TOUCHSCREENGUI
m_cache_hold_aux1 = false; // This is initialised properly later
#endif
m_invert_mouse = g_settings->getBool("invert_mouse");
m_first_loop_after_window_activation = true;
+#ifdef HAVE_TOUCHSCREENGUI
+ m_touch_use_crosshair = g_settings->getBool("touch_use_crosshair");
+#endif
+
g_client_translations->clear();
// address can change if simple_singleplayer_mode
void Game::run()
{
ProfilerGraph graph;
- RunStats stats = { 0 };
- CameraOrientation cam_view_target = { 0 };
- CameraOrientation cam_view = { 0 };
- FpsControl draw_times = { 0 };
+ RunStats stats = {};
+ CameraOrientation cam_view_target = {};
+ CameraOrientation cam_view = {};
+ FpsControl draw_times;
f32 dtime; // in seconds
/* Clear the profiler */
Profiler::GraphValues dummyvalues;
g_profiler->graphGet(dummyvalues);
- draw_times.last_time = m_rendering_engine->get_timer_time();
+ draw_times.reset();
set_light_table(g_settings->getFloat("display_gamma"));
-#ifdef __ANDROID__
+#ifdef HAVE_TOUCHSCREENGUI
m_cache_hold_aux1 = g_settings->getBool("fast_move")
&& client->checkPrivilege("fast");
#endif
- irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"),
+ v2u32 previous_screen_size(g_settings->getU16("screen_w"),
g_settings->getU16("screen_h"));
while (m_rendering_engine->run()
&& !(*kill || g_gamecallback->shutdown_requested
|| (server && server->isShutdownRequested()))) {
- const irr::core::dimension2d<u32> ¤t_screen_size =
- m_rendering_engine->get_video_driver()->getScreenSize();
+ // Calculate dtime =
+ // m_rendering_engine->run() from this iteration
+ // + Sleep time until the wanted FPS are reached
+ draw_times.limit(device, &dtime);
+
+ const auto current_dynamic_info = getCurrentDynamicInfo();
+ if (!current_dynamic_info.equal(client_display_info)) {
+ client_display_info = current_dynamic_info;
+ dynamic_info_send_timer = 0.2f;
+ }
+
+ if (dynamic_info_send_timer > 0) {
+ dynamic_info_send_timer -= dtime;
+ if (dynamic_info_send_timer <= 0) {
+ client->sendUpdateClientInfo(current_dynamic_info);
+ }
+ }
+
+ const auto ¤t_screen_size = current_dynamic_info.render_target_size;
+
// Verify if window size has changed and save it if it's the case
// Ensure evaluating settings->getBool after verifying screensize
// First condition is cheaper
if (previous_screen_size != current_screen_size &&
current_screen_size != irr::core::dimension2d<u32>(0,0) &&
g_settings->getBool("autosave_screensize")) {
- g_settings->setU16("screen_w", current_screen_size.Width);
- g_settings->setU16("screen_h", current_screen_size.Height);
+ g_settings->setU16("screen_w", current_screen_size.X);
+ g_settings->setU16("screen_h", current_screen_size.Y);
previous_screen_size = current_screen_size;
}
- // Calculate dtime =
- // m_rendering_engine->run() from this iteration
- // + Sleep time until the wanted FPS are reached
- limitFps(&draw_times, &dtime);
-
// Prepare render data for next iteration
updateStats(&stats, draw_times, dtime);
processQueues();
m_game_ui->clearInfoText();
- hud->resizeHotbar();
-
updateProfilers(stats, draw_times, dtime);
processUserInput(dtime);
cam_view.camera_pitch += (cam_view_target.camera_pitch -
cam_view.camera_pitch) * m_cache_cam_smoothing;
updatePlayerControl(cam_view);
- step(&dtime);
+
+ {
+ bool was_paused = m_is_paused;
+ m_is_paused = simple_singleplayer_mode && g_menumgr.pausesGame();
+ if (m_is_paused)
+ dtime = 0.0f;
+
+ if (!was_paused && m_is_paused)
+ pauseAnimation();
+ else if (was_paused && !m_is_paused)
+ resumeAnimation();
+ }
+
+ if (!m_is_paused)
+ step(dtime);
processClientEvents(&cam_view_target);
updateDebugState();
- updateCamera(draw_times.busy_time, dtime);
+ updateCamera(dtime);
updateSound(dtime);
- processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud,
- m_game_ui->m_flags.show_basic_debug);
+ processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud);
updateFrame(&graph, &stats, dtime, cam_view);
updateProfilerGraphs(&graph);
void Game::shutdown()
{
m_rendering_engine->finalize();
-#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8
- if (g_settings->get("3d_mode") == "pageflip") {
- driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS);
- }
-#endif
+
auto formspec = m_game_ui->getFormspecGUI();
if (formspec)
formspec->quitMenu();
}
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- *error_message = "Unable to listen on " +
- bind_addr.serializeString() +
- " because IPv6 is disabled";
+ *error_message = fmtgettext("Unable to listen on %s because IPv6 is disabled",
+ bind_addr.serializeString().c_str());
errorstream << *error_message << std::endl;
return false;
}
if (!could_connect) {
if (error_message->empty() && !connect_aborted) {
// Should not happen if error messages are set properly
- *error_message = "Connection failed for unknown reason";
+ *error_message = gettext("Connection failed for unknown reason");
errorstream << *error_message << std::endl;
}
return false;
if (!getServerContent(&connect_aborted)) {
if (error_message->empty() && !connect_aborted) {
// Should not happen if error messages are set properly
- *error_message = "Connection failed for unknown reason";
+ *error_message = gettext("Connection failed for unknown reason");
errorstream << *error_message << std::endl;
}
return false;
/* Camera
*/
camera = new Camera(*draw_control, client, m_rendering_engine);
- if (!camera->successfullyCreated(*error_message))
- return false;
+ if (client->modsLoaded())
+ client->getScript()->on_camera_ready(camera);
client->setCamera(camera);
+#ifdef HAVE_TOUCHSCREENGUI
+ if (g_touchscreengui) {
+ g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
+ }
+#endif
/* Clouds
*/
str += L" [";
str += text;
str += L"]";
- delete text;
+ delete[] text;
}
str += L" [";
str += driver->getName();
connect_address.Resolve(start_data.address.c_str());
if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
- //connect_address.Resolve("localhost");
if (connect_address.isIPv6()) {
IPv6AddressBytes addr_bytes;
addr_bytes.bytes[15] = 1;
local_server_mode = true;
}
} catch (ResolveError &e) {
- *error_message = std::string("Couldn't resolve address: ") + e.what();
+ *error_message = fmtgettext("Couldn't resolve address: %s", e.what());
+
errorstream << *error_message << std::endl;
return false;
}
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- *error_message = "Unable to connect to " +
- connect_address.serializeString() +
- " because IPv6 is disabled";
+ *error_message = fmtgettext("Unable to connect to %s because IPv6 is disabled", connect_address.serializeString().c_str());
errorstream << *error_message << std::endl;
return false;
}
- client = new Client(start_data.name.c_str(),
- start_data.password, start_data.address,
- *draw_control, texture_src, shader_src,
- itemdef_manager, nodedef_manager, sound, eventmgr,
- m_rendering_engine, connect_address.isIPv6(), m_game_ui.get());
+ try {
+ client = new Client(start_data.name.c_str(),
+ start_data.password, start_data.address,
+ *draw_control, texture_src, shader_src,
+ itemdef_manager, nodedef_manager, sound, eventmgr,
+ m_rendering_engine, connect_address.isIPv6(), m_game_ui.get(),
+ start_data.allow_login_or_register);
+ client->migrateModStorage();
+ } catch (const BaseException &e) {
+ *error_message = fmtgettext("Error creating client: %s", e.what());
+ errorstream << *error_message << std::endl;
+ return false;
+ }
client->m_simple_singleplayer_mode = simple_singleplayer_mode;
infostream << "Connecting to server at ";
- connect_address.print(&infostream);
+ connect_address.print(infostream);
infostream << std::endl;
client->connect(connect_address,
try {
input->clear();
- FpsControl fps_control = { 0 };
+ FpsControl fps_control;
f32 dtime;
f32 wait_time = 0; // in seconds
- fps_control.last_time = m_rendering_engine->get_timer_time();
+ fps_control.reset();
while (m_rendering_engine->run()) {
- limitFps(&fps_control, &dtime);
+ fps_control.limit(device, &dtime);
// Update client and server
client->step(dtime);
break;
if (client->accessDenied()) {
- *error_message = "Access denied. Reason: "
- + client->accessDeniedReason();
+ *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
*reconnect_requested = client->reconnectRequested();
errorstream << *error_message << std::endl;
break;
break;
}
- if (client->m_is_registration_confirmation_state) {
- if (registration_confirmation_shown) {
- // Keep drawing the GUI
- m_rendering_engine->draw_menu_scene(guienv, dtime, true);
- } else {
- registration_confirmation_shown = true;
- (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
- &g_menumgr, client, start_data.name, start_data.password,
- connection_aborted, texture_src))->drop();
- }
- } else {
- wait_time += dtime;
- // Only time out if we aren't waiting for the server we started
- if (!start_data.address.empty() && wait_time > 10) {
- *error_message = "Connection timed out.";
- errorstream << *error_message << std::endl;
- break;
- }
-
- // Update status
- showOverlayMessage(N_("Connecting to server..."), dtime, 20);
+ wait_time += dtime;
+ // Only time out if we aren't waiting for the server we started
+ if (!start_data.address.empty() && wait_time > 10) {
+ *error_message = gettext("Connection timed out.");
+ errorstream << *error_message << std::endl;
+ break;
}
+
+ // Update status
+ showOverlayMessage(N_("Connecting to server..."), dtime, 20);
}
} catch (con::PeerNotFoundException &e) {
// TODO: Should something be done here? At least an info/error
{
input->clear();
- FpsControl fps_control = { 0 };
+ FpsControl fps_control;
f32 dtime; // in seconds
- fps_control.last_time = m_rendering_engine->get_timer_time();
+ fps_control.reset();
while (m_rendering_engine->run()) {
- limitFps(&fps_control, &dtime);
+ fps_control.limit(device, &dtime);
// Update client and server
client->step(dtime);
return false;
if (client->getState() < LC_Init) {
- *error_message = "Client disconnected";
+ *error_message = gettext("Client disconnected");
errorstream << *error_message << std::endl;
return false;
}
inline bool Game::checkConnection()
{
if (client->accessDenied()) {
- *error_message = "Access denied. Reason: "
- + client->accessDeniedReason();
+ *error_message = fmtgettext("Access denied. Reason: %s", client->accessDeniedReason().c_str());
*reconnect_requested = client->reconnectRequested();
errorstream << *error_message << std::endl;
return false;
void Game::updateDebugState()
{
- bool has_basic_debug = client->checkPrivilege("basic_debug");
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+
+ // debug UI and wireframe
bool has_debug = client->checkPrivilege("debug");
+ bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
if (m_game_ui->m_flags.show_basic_debug) {
- if (!has_basic_debug) {
+ if (!has_basic_debug)
m_game_ui->m_flags.show_basic_debug = false;
- }
} else if (m_game_ui->m_flags.show_minimal_debug) {
- if (has_basic_debug) {
+ if (has_basic_debug)
m_game_ui->m_flags.show_basic_debug = true;
- }
}
if (!has_basic_debug)
hud->disableBlockBounds();
if (!has_debug)
draw_control->show_wireframe = false;
+
+ // noclip
+ draw_control->allow_noclip = m_cache_enable_noclip && client->checkPrivilege("noclip");
}
void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
}
// Update update graphs
- g_profiler->graphAdd("Time non-rendering [ms]",
+ g_profiler->graphAdd("Time non-rendering [us]",
draw_times.busy_time - stats.drawtime);
- g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time);
+ g_profiler->graphAdd("Sleep [us]", draw_times.sleep_time);
g_profiler->graphAdd("FPS", 1.0f / dtime);
}
/* Busytime average and jitter calculation
*/
jp = &stats->busy_time_jitter;
- jp->avg = jp->avg + draw_times.busy_time * 0.02;
+ jp->avg = jp->avg + draw_times.getBusyMs() * 0.02;
- jitter = draw_times.busy_time - jp->avg;
+ jitter = draw_times.getBusyMs() - jp->avg;
if (jitter > jp->max)
jp->max = jitter;
else if (g_touchscreengui) {
/* on touchscreengui step may generate own input events which ain't
* what we want in case we just did clear them */
+ g_touchscreengui->show();
g_touchscreengui->step(dtime);
}
#endif
#endif
// Increase timer for double tap of "keymap_jump"
- if (m_cache_doubletap_jump && runData.jump_timer <= 0.2f)
- runData.jump_timer += dtime;
+ if (m_cache_doubletap_jump && runData.jump_timer_up <= 0.2f)
+ runData.jump_timer_up += dtime;
+ if (m_cache_doubletap_jump && runData.jump_timer_down <= 0.4f)
+ runData.jump_timer_down += dtime;
processKeyInput();
processItemSelection(&runData.new_playeritem);
if (client->modsLoaded())
openConsole(0.2, L".");
else
- m_game_ui->showStatusText(wgettext("Client side scripting is disabled"));
+ m_game_ui->showTranslatedStatusText("Client side scripting is disabled");
} else if (wasKeyDown(KeyType::CONSOLE)) {
openConsole(core::clamp(g_settings->getFloat("console_height"), 0.1f, 1.0f));
} else if (wasKeyDown(KeyType::FREEMOVE)) {
}
} else if (wasKeyDown(KeyType::INC_VOLUME)) {
if (g_settings->getBool("enable_sound")) {
- float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
+ float new_volume = g_settings->getFloat("sound_volume", 0.0f, 0.9f) + 0.1f;
g_settings->setFloat("sound_volume", new_volume);
std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
m_game_ui->showStatusText(msg);
}
} else if (wasKeyDown(KeyType::DEC_VOLUME)) {
if (g_settings->getBool("enable_sound")) {
- float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
+ float new_volume = g_settings->getFloat("sound_volume", 0.1f, 1.0f) - 0.1f;
g_settings->setFloat("sound_volume", new_volume);
std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100));
m_game_ui->showStatusText(msg);
} else if (wasKeyDown(KeyType::MINIMAP)) {
toggleMinimap(isKeyDown(KeyType::SNEAK));
} else if (wasKeyDown(KeyType::TOGGLE_CHAT)) {
- m_game_ui->toggleChat();
+ m_game_ui->toggleChat(client);
} else if (wasKeyDown(KeyType::TOGGLE_FOG)) {
toggleFog();
} else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
if (!isKeyDown(KeyType::JUMP) && runData.reset_jump_timer) {
runData.reset_jump_timer = false;
- runData.jump_timer = 0.0f;
+ runData.jump_timer_up = 0.0f;
}
if (quicktune->hasMessage()) {
/* Item selection using mouse wheel
*/
*new_playeritem = player->getWieldIndex();
-
s32 wheel = input->getMouseWheel();
u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
player->hud_hotbar_itemcount - 1);
break;
}
}
+
+ // Clamp selection again in case it wasn't changed but max_item was
+ *new_playeritem = MYMIN(*new_playeritem, max_item);
}
InventoryLocation inventoryloc;
inventoryloc.setCurrentPlayer();
- if (!client->modsLoaded()
- || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
- TextDest *txt_dst = new TextDestPlayerInventory(client);
- auto *&formspec = m_game_ui->updateFormspec("");
- GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
- &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
+ if (client->modsLoaded() && client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
+ delete fs_src;
+ return;
+ }
- formspec->setFormSpec(fs_src->getForm(), inventoryloc);
+ if (fs_src->getForm().empty()) {
+ delete fs_src;
+ return;
}
+
+ TextDest *txt_dst = new TextDestPlayerInventory(client);
+ auto *&formspec = m_game_ui->updateFormspec("");
+ GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
+ &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
+
+ formspec->setFormSpec(fs_src->getForm(), inventoryloc);
}
void Game::toggleFreeMoveAlt()
{
- if (m_cache_doubletap_jump && runData.jump_timer < 0.2f)
+ if (!runData.reset_jump_timer) {
+ runData.jump_timer_down_before = runData.jump_timer_down;
+ runData.jump_timer_down = 0.0f;
+ }
+
+ // key down (0.2 s max.), then key up (0.2 s max.), then key down
+ if (m_cache_doubletap_jump && runData.jump_timer_up < 0.2f &&
+ runData.jump_timer_down_before < 0.4f) // 0.2 + 0.2
toggleFreeMove();
runData.reset_jump_timer = true;
m_game_ui->showTranslatedStatusText("Fast mode disabled");
}
-#ifdef __ANDROID__
+#ifdef HAVE_TOUCHSCREENGUI
m_cache_hold_aux1 = fast_move && has_fast_privs;
#endif
}
void Game::toggleBlockBounds()
{
- if (client->checkPrivilege("basic_debug")) {
- enum Hud::BlockBoundsMode newmode = hud->toggleBlockBounds();
- switch (newmode) {
- case Hud::BLOCK_BOUNDS_OFF:
- m_game_ui->showTranslatedStatusText("Block bounds hidden");
- break;
- case Hud::BLOCK_BOUNDS_CURRENT:
- m_game_ui->showTranslatedStatusText("Block bounds shown for current block");
- break;
- case Hud::BLOCK_BOUNDS_NEAR:
- m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks");
- break;
- case Hud::BLOCK_BOUNDS_MAX:
- m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks");
- break;
- default:
- break;
- }
-
- } else {
- m_game_ui->showTranslatedStatusText("Can't show block bounds (need 'basic_debug' privilege)");
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ if (!(client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG))) {
+ m_game_ui->showTranslatedStatusText("Can't show block bounds (disabled by mod or game)");
+ return;
+ }
+ enum Hud::BlockBoundsMode newmode = hud->toggleBlockBounds();
+ switch (newmode) {
+ case Hud::BLOCK_BOUNDS_OFF:
+ m_game_ui->showTranslatedStatusText("Block bounds hidden");
+ break;
+ case Hud::BLOCK_BOUNDS_CURRENT:
+ m_game_ui->showTranslatedStatusText("Block bounds shown for current block");
+ break;
+ case Hud::BLOCK_BOUNDS_NEAR:
+ m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks");
+ break;
+ case Hud::BLOCK_BOUNDS_MAX:
+ m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks");
+ break;
+ default:
+ break;
}
}
void Game::toggleDebug()
{
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ bool has_debug = client->checkPrivilege("debug");
+ bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
// Initial: No debug info
// 1x toggle: Debug text
// 2x toggle: Debug text with profiler graph
// The debug text can be in 2 modes: minimal and basic.
// * Minimal: Only technical client info that not gameplay-relevant
// * Basic: Info that might give gameplay advantage, e.g. pos, angle
- // Basic mode is used when player has "basic_debug" priv,
+ // Basic mode is used when player has the debug HUD flag set,
// otherwise the Minimal mode is used.
if (!m_game_ui->m_flags.show_minimal_debug) {
m_game_ui->m_flags.show_minimal_debug = true;
- if (client->checkPrivilege("basic_debug")) {
+ if (has_basic_debug)
m_game_ui->m_flags.show_basic_debug = true;
- }
m_game_ui->m_flags.show_profiler_graph = false;
draw_control->show_wireframe = false;
m_game_ui->showTranslatedStatusText("Debug info shown");
} else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) {
- if (client->checkPrivilege("basic_debug")) {
+ if (has_basic_debug)
m_game_ui->m_flags.show_basic_debug = true;
- }
m_game_ui->m_flags.show_profiler_graph = true;
m_game_ui->showTranslatedStatusText("Profiler graph shown");
} else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) {
- if (client->checkPrivilege("basic_debug")) {
+ if (has_basic_debug)
m_game_ui->m_flags.show_basic_debug = true;
- }
m_game_ui->m_flags.show_profiler_graph = false;
draw_control->show_wireframe = true;
m_game_ui->showTranslatedStatusText("Wireframe shown");
m_game_ui->m_flags.show_basic_debug = false;
m_game_ui->m_flags.show_profiler_graph = false;
draw_control->show_wireframe = false;
- if (client->checkPrivilege("debug")) {
+ if (has_debug) {
m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden");
} else {
m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden");
void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
{
+#if !defined(__ANDROID__) && IRRLICHT_VERSION_MT_REVISION >= 9
+ if (isMenuActive())
+ device->getCursorControl()->setRelativeMode(false);
+ else
+ device->getCursorControl()->setRelativeMode(true);
+#endif
+
if ((device->isWindowActive() && device->isWindowFocused()
&& !isMenuActive()) || input->isRandom()) {
return tan(fov_y / 2.0f) * 1.3763818698f;
}
+ClientDynamicInfo Game::getCurrentDynamicInfo() const
+{
+ v2u32 screen_size = RenderingEngine::getWindowSize();
+ f32 density = RenderingEngine::getDisplayDensity();
+ f32 gui_scaling = g_settings->getFloat("gui_scaling") * density;
+ f32 hud_scaling = g_settings->getFloat("hud_scaling") * density;
+
+ return {
+ screen_size, gui_scaling, hud_scaling,
+ ClientDynamicInfo::calculateMaxFSSize(screen_size)
+ };
+}
+
void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
{
#ifdef HAVE_TOUCHSCREENGUI
//TimeTaker tt("update player control", NULL, PRECISION_NANO);
PlayerControl control(
+ isKeyDown(KeyType::FORWARD),
+ isKeyDown(KeyType::BACKWARD),
+ isKeyDown(KeyType::LEFT),
+ isKeyDown(KeyType::RIGHT),
isKeyDown(KeyType::JUMP) || player->getAutojump(),
isKeyDown(KeyType::AUX1),
isKeyDown(KeyType::SNEAK),
input->getMovementDirection()
);
- // autoforward if set: move towards pointed position at maximum speed
+ // autoforward if set: move at maximum speed
if (player->getPlayerSettings().continuous_forward &&
client->activeObjectsReceived() && !player->isDead()) {
control.movement_speed = 1.0f;
- control.movement_direction = 0.0f;
+ // sideways movement only
+ float dx = sin(control.movement_direction);
+ control.movement_direction = atan2(dx, 1.0f);
}
-#ifdef ANDROID
- /* For Android, simulate holding down AUX1 (fast move) if the user has
+#ifdef HAVE_TOUCHSCREENGUI
+ /* For touch, simulate holding down AUX1 (fast move) if the user has
* the fast_move setting toggled on. If there is an aux1 key defined for
- * Android then its meaning is inverted (i.e. holding aux1 means walk and
+ * touch then its meaning is inverted (i.e. holding aux1 means walk and
* not fast)
*/
if (m_cache_hold_aux1) {
}
#endif
- u32 keypress_bits = (
- ( (u32)(control.jump & 0x1) << 4) |
- ( (u32)(control.aux1 & 0x1) << 5) |
- ( (u32)(control.sneak & 0x1) << 6) |
- ( (u32)(control.dig & 0x1) << 7) |
- ( (u32)(control.place & 0x1) << 8) |
- ( (u32)(control.zoom & 0x1) << 9)
- );
-
- // Set direction keys to ensure mod compatibility
- if (control.movement_speed > 0.001f) {
- float absolute_direction;
-
- // Check in original orientation (absolute value indicates forward / backward)
- absolute_direction = abs(control.movement_direction);
- if (absolute_direction < (3.0f / 8.0f * M_PI))
- keypress_bits |= (u32)(0x1 << 0); // Forward
- if (absolute_direction > (5.0f / 8.0f * M_PI))
- keypress_bits |= (u32)(0x1 << 1); // Backward
-
- // Rotate entire coordinate system by 90 degrees (absolute value indicates left / right)
- absolute_direction = control.movement_direction + M_PI_2;
- if (absolute_direction >= M_PI)
- absolute_direction -= 2 * M_PI;
- absolute_direction = abs(absolute_direction);
- if (absolute_direction < (3.0f / 8.0f * M_PI))
- keypress_bits |= (u32)(0x1 << 2); // Left
- if (absolute_direction > (5.0f / 8.0f * M_PI))
- keypress_bits |= (u32)(0x1 << 3); // Right
- }
-
client->setPlayerControl(control);
- player->keyPressed = keypress_bits;
//tt.stop();
}
-inline void Game::step(f32 *dtime)
+inline void Game::step(f32 dtime)
{
- bool can_be_and_is_paused =
- (simple_singleplayer_mode && g_menumgr.pausesGame());
+ if (server)
+ server->step(dtime);
- if (can_be_and_is_paused) { // This is for a singleplayer server
- *dtime = 0; // No time passes
- } else {
- if (simple_singleplayer_mode && !paused_animated_nodes.empty())
- resumeAnimation();
-
- if (server)
- server->step(*dtime);
-
- client->step(*dtime);
- }
+ client->step(dtime);
}
static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) {
if (client->modsLoaded())
client->getScript()->on_damage_taken(event->player_damage.amount);
+ if (!event->player_damage.effect)
+ return;
+
// Damage flash and hurt tilt are not used at death
if (client->getHP() > 0) {
LocalPlayer *player = client->getEnv().getLocalPlayer();
);
}
+ // Orbit Tilt:
+ sky->setBodyOrbitTilt(event->set_sky->body_orbit_tilt);
+
delete event->set_sky;
}
void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam)
{
sky->setStarsVisible(event->star_params->visible);
- sky->setStarCount(event->star_params->count, false);
+ sky->setStarCount(event->star_params->count);
sky->setStarColor(event->star_params->starcolor);
sky->setStarScale(event->star_params->scale);
+ sky->setStarDayOpacity(event->star_params->day_opacity);
delete event->star_params;
}
m_game_ui->updateChatSize();
}
-void Game::updateCamera(u32 busy_time, f32 dtime)
+void Game::updateCamera(f32 dtime)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
camera->toggleCameraMode();
+#ifdef HAVE_TOUCHSCREENGUI
+ if (g_touchscreengui)
+ g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
+#endif
+
// Make the player visible depending on camera mode.
playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval;
tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
- camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio);
+ camera->update(player, dtime, tool_reload_ratio);
camera->step(dtime);
- v3f camera_position = camera->getPosition();
- v3f camera_direction = camera->getDirection();
f32 camera_fov = camera->getFovMax();
v3s16 camera_offset = camera->getOffset();
m_camera_offset_changed = (camera_offset != old_camera_offset);
if (!m_flags.disable_camera_update) {
+ v3f camera_position = camera->getPosition();
+ v3f camera_direction = camera->getDirection();
+
client->getEnv().getClientMap().updateCamera(camera_position,
camera_direction, camera_fov, camera_offset);
}
-void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
+void Game::processPlayerInteraction(f32 dtime, bool show_hud)
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
shootline.end = shootline.start + camera_direction * BS * d;
#ifdef HAVE_TOUCHSCREENGUI
-
- if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
+ if (g_touchscreengui && isNoCrosshairAllowed()) {
shootline = g_touchscreengui->getShootline();
// Scale shootline to the acual distance the player can reach
- shootline.end = shootline.start
- + shootline.getVector().normalize() * BS * d;
+ shootline.end = shootline.start +
+ shootline.getVector().normalize() * BS * d;
shootline.start += intToFloat(camera_offset, BS);
shootline.end += intToFloat(camera_offset, BS);
}
-
#endif
PointedThing pointed = updatePointedThing(shootline,
!runData.btn_down_for_dig,
camera_offset);
- if (pointed != runData.pointed_old) {
+ if (pointed != runData.pointed_old)
infostream << "Pointing at " << pointed.dump() << std::endl;
- hud->updateSelectionMesh(camera_offset);
- }
+
+ // Note that updating the selection mesh every frame is not particularly efficient,
+ // but the halo rendering code is already inefficient so there's no point in optimizing it here
+ hud->updateSelectionMesh(camera_offset);
// Allow digging again if button is not pressed
if (runData.digging_blocked && !isKeyDown(KeyType::DIG))
runData.punching = false;
- soundmaker->m_player_leftpunch_sound.name = "";
+ soundmaker->m_player_leftpunch_sound = SimpleSoundSpec();
+ soundmaker->m_player_leftpunch_sound2 = pointed.type != POINTEDTHING_NOTHING ?
+ selected_def.sound_use : selected_def.sound_use_air;
// Prepare for repeating, unless we're not supposed to
if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place"))
handlePointingAtNode(pointed, selected_item, hand_item, dtime);
} else if (pointed.type == POINTEDTHING_OBJECT) {
v3f player_position = player->getPosition();
- handlePointingAtObject(pointed, tool_item, player_position, show_debug);
+ bool basic_debug_allowed = client->checkPrivilege("debug") || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
+ handlePointingAtObject(pointed, tool_item, player_position,
+ m_game_ui->m_flags.show_basic_debug && basic_debug_allowed);
} else if (isKeyDown(KeyType::DIG)) {
// When button is held down in air, show continuous animation
runData.punching = true;
{
std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
selectionboxes->clear();
- hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0));
+ hud->setSelectedFaceNormal(v3f());
static thread_local const bool show_entity_selectionbox = g_settings->getBool(
"show_entity_selectionbox");
v3f pos = runData.selected_object->getPosition();
selectionboxes->push_back(aabb3f(selection_box));
hud->setSelectionPos(pos, camera_offset);
+ GenericCAO* gcao = dynamic_cast<GenericCAO*>(runData.selected_object);
+ if (gcao != nullptr && gcao->getProperties().rotate_selectionbox)
+ hud->setSelectionRotation(gcao->getSceneNode()->getAbsoluteTransformation().getRotationDegrees());
+ else
+ hud->setSelectionRotation(v3f());
}
+ hud->setSelectedFaceNormal(result.raw_intersection_normal);
} else if (result.type == POINTEDTHING_NODE) {
// Update selection boxes
MapNode n = map.getNode(result.node_undersurface);
}
hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
camera_offset);
- hud->setSelectedFaceNormal(v3f(
- result.intersection_normal.X,
- result.intersection_normal.Y,
- result.intersection_normal.Z));
+ hud->setSelectionRotation(v3f());
+ hud->setSelectedFaceNormal(result.intersection_normal);
}
// Update selection mesh light level and vertex colors
final_color_blend(&c, light_level, daynight_ratio);
// Modify final color a bit with time
- u32 timer = porting::getTimeMs() % 5000;
+ u32 timer = client->getEnv().getFrameTime() % 5000;
float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5));
float sin_r = 0.08f * std::sin(timerf);
float sin_g = 0.08f * std::sin(timerf + irr::core::PI * 0.5f);
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
{
v3s16 nodepos = pointed.node_undersurface;
- v3s16 neighbourpos = pointed.node_abovesurface;
+ v3s16 neighborpos = pointed.node_abovesurface;
/*
Check information text of node
} else {
MapNode n = map.getNode(nodepos);
- if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
- m_game_ui->setInfoText(L"Unknown node: " +
- utf8_to_wide(nodedef_manager->get(n).name));
+ if (nodedef_manager->get(n).name == "unknown") {
+ m_game_ui->setInfoText(L"Unknown node");
}
}
// And also set the sound and send the interact
// But first check for meta formspec and rightclickable
auto &def = selected_item.getDefinition(itemdef_manager);
- bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos,
+ bool placed = nodePlacement(def, selected_item, nodepos, neighborpos,
pointed, meta);
if (placed && client->modsLoaded())
}
bool Game::nodePlacement(const ItemDefinition &selected_def,
- const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos,
+ const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighborpos,
const PointedThing &pointed, const NodeMetadata *meta)
{
const auto &prediction = selected_def.node_placement_prediction;
verbosestream << "Node placement prediction for "
<< selected_def.name << " is " << prediction << std::endl;
- v3s16 p = neighbourpos;
+ v3s16 p = neighborpos;
// Place inside node itself if buildable_to
MapNode n_under = map.getNode(nodepos, &is_valid_position);
const ContentFeatures &predicted_f = nodedef->get(id);
- // Predict param2 for facedir and wallmounted nodes
- // Compare core.item_place_node() for what the server does
- u8 param2 = 0;
+ // Compare core.item_place_node() for what the server does with param2
+ MapNode predicted_node(id, 0, 0);
const u8 place_param2 = selected_def.place_param2;
if (place_param2) {
- param2 = place_param2;
+ predicted_node.setParam2(place_param2);
} else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
- v3s16 dir = nodepos - neighbourpos;
+ v3s16 dir = nodepos - neighborpos;
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
- param2 = dir.Y < 0 ? 1 : 0;
+ predicted_node.setParam2(dir.Y < 0 ? 1 : 0);
} else if (abs(dir.X) > abs(dir.Z)) {
- param2 = dir.X < 0 ? 3 : 2;
+ predicted_node.setParam2(dir.X < 0 ? 3 : 2);
} else {
- param2 = dir.Z < 0 ? 5 : 4;
+ predicted_node.setParam2(dir.Z < 0 ? 5 : 4);
}
} else if (predicted_f.param_type_2 == CPT2_FACEDIR ||
- predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
+ predicted_f.param_type_2 == CPT2_COLORED_FACEDIR ||
+ predicted_f.param_type_2 == CPT2_4DIR ||
+ predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
if (abs(dir.X) > abs(dir.Z)) {
- param2 = dir.X < 0 ? 3 : 1;
+ predicted_node.setParam2(dir.X < 0 ? 3 : 1);
} else {
- param2 = dir.Z < 0 ? 2 : 0;
+ predicted_node.setParam2(dir.Z < 0 ? 2 : 0);
}
}
// Check attachment if node is in group attached_node
- if (itemgroup_get(predicted_f.groups, "attached_node") != 0) {
- const static v3s16 wallmounted_dirs[8] = {
- v3s16(0, 1, 0),
- v3s16(0, -1, 0),
- v3s16(1, 0, 0),
- v3s16(-1, 0, 0),
- v3s16(0, 0, 1),
- v3s16(0, 0, -1),
- };
+ int an = itemgroup_get(predicted_f.groups, "attached_node");
+ if (an != 0) {
v3s16 pp;
- if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
- predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
- pp = p + wallmounted_dirs[param2];
- else
+ if (an == 3) {
pp = p + v3s16(0, -1, 0);
+ } else if (an == 4) {
+ pp = p + v3s16(0, 1, 0);
+ } else if (an == 2) {
+ if (predicted_f.param_type_2 == CPT2_FACEDIR ||
+ predicted_f.param_type_2 == CPT2_COLORED_FACEDIR ||
+ predicted_f.param_type_2 == CPT2_4DIR ||
+ predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
+ pp = p + facedir_dirs[predicted_node.getFaceDir(nodedef)];
+ } else {
+ pp = p;
+ }
+ } else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
+ predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
+ pp = p + predicted_node.getWallMountedDir(nodedef);
+ } else {
+ pp = p + v3s16(0, -1, 0);
+ }
if (!nodedef->get(map.getNode(pp)).walkable) {
soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
// Apply color
if (!place_param2 && (predicted_f.param_type_2 == CPT2_COLOR
|| predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
+ || predicted_f.param_type_2 == CPT2_COLORED_4DIR
|| predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
const auto &indexstr = selected_item.metadata.
getString("palette_index", 0);
if (!indexstr.empty()) {
s32 index = mystoi(indexstr);
if (predicted_f.param_type_2 == CPT2_COLOR) {
- param2 = index;
+ predicted_node.setParam2(index);
} else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
// param2 = pure palette index + other
- param2 = (index & 0xf8) | (param2 & 0x07);
+ predicted_node.setParam2((index & 0xf8) | (predicted_node.getParam2() & 0x07));
} else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
// param2 = pure palette index + other
- param2 = (index & 0xe0) | (param2 & 0x1f);
+ predicted_node.setParam2((index & 0xe0) | (predicted_node.getParam2() & 0x1f));
+ } else if (predicted_f.param_type_2 == CPT2_COLORED_4DIR) {
+ // param2 = pure palette index + other
+ predicted_node.setParam2((index & 0xfc) | (predicted_node.getParam2() & 0x03));
}
}
}
// Add node to client map
- MapNode n(id, 0, param2);
-
try {
LocalPlayer *player = client->getEnv().getLocalPlayer();
- // Dont place node when player would be inside new node
+ // Don't place node when player would be inside new node
// NOTE: This is to be eventually implemented by a mod as client-side Lua
- if (!nodedef->get(n).walkable ||
+ if (!predicted_f.walkable ||
g_settings->getBool("enable_build_where_you_stand") ||
(client->checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
- (nodedef->get(n).walkable &&
- neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
- neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
+ (predicted_f.walkable &&
+ neighborpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
+ neighborpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
// This triggers the required mesh update too
- client->addNode(p, n);
+ client->addNode(p, predicted_node);
// Report to server
client->interact(INTERACT_PLACE, pointed);
// A node is predicted, also play a sound
// See also: serverpackethandle.cpp, action == 2
LocalPlayer *player = client->getEnv().getLocalPlayer();
ClientMap &map = client->getEnv().getClientMap();
- MapNode n = client->getEnv().getClientMap().getNode(nodepos);
+ MapNode n = map.getNode(nodepos);
+ const auto &features = nodedef_manager->get(n);
// NOTE: Similar piece of code exists on the server side for
// cheat detection.
// Get digging parameters
- DigParams params = getDigParams(nodedef_manager->get(n).groups,
- &selected_item.getToolCapabilities(itemdef_manager));
+ DigParams params = getDigParams(features.groups,
+ &selected_item.getToolCapabilities(itemdef_manager),
+ selected_item.wear);
// If can't dig, try hand
if (!params.diggable) {
- params = getDigParams(nodedef_manager->get(n).groups,
+ params = getDigParams(features.groups,
&hand_item.getToolCapabilities(itemdef_manager));
}
runData.dig_time_complete = params.time;
if (m_cache_enable_particles) {
- const ContentFeatures &features = client->getNodeDefManager()->get(n);
client->getParticleManager()->addNodeParticle(client,
player, nodepos, n, features);
}
runData.dig_instantly = runData.dig_time_complete == 0;
if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n))
return;
+
client->interact(INTERACT_START_DIGGING, pointed);
runData.digging = true;
runData.btn_down_for_dig = true;
runData.dig_index = crack_animation_length;
}
- SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
+ const auto &sound_dig = features.sound_dig;
if (sound_dig.exists() && params.diggable) {
if (sound_dig.name == "__group") {
// Don't show cracks if not diggable
if (runData.dig_time_complete >= 100000.0) {
} else if (runData.dig_index < crack_animation_length) {
- //TimeTaker timer("client.setTempMod");
- //infostream<<"dig_index="<<dig_index<<std::endl;
client->setCrack(runData.dig_index, nodepos);
} else {
infostream << "Digging completed" << std::endl;
else if (runData.dig_instantly)
runData.nodig_delay_timer = 0.15;
- bool is_valid_position;
- MapNode wasnode = map.getNode(nodepos, &is_valid_position);
- if (is_valid_position) {
- if (client->modsLoaded() &&
- client->getScript()->on_dignode(nodepos, wasnode)) {
- return;
- }
+ if (client->modsLoaded() &&
+ client->getScript()->on_dignode(nodepos, n)) {
+ return;
+ }
- const ContentFeatures &f = client->ndef()->get(wasnode);
- if (f.node_dig_prediction == "air") {
- client->removeNode(nodepos);
- } else if (!f.node_dig_prediction.empty()) {
- content_t id;
- bool found = client->ndef()->getId(f.node_dig_prediction, id);
- if (found)
- client->addNode(nodepos, id, true);
- }
- // implicit else: no prediction
+ if (features.node_dig_prediction == "air") {
+ client->removeNode(nodepos);
+ } else if (!features.node_dig_prediction.empty()) {
+ content_t id;
+ bool found = nodedef_manager->getId(features.node_dig_prediction, id);
+ if (found)
+ client->addNode(nodepos, id, true);
}
+ // implicit else: no prediction
client->interact(INTERACT_DIGGING_COMPLETED, pointed);
if (m_cache_enable_particles) {
- const ContentFeatures &features =
- client->getNodeDefManager()->get(wasnode);
client->getParticleManager()->addDiggingParticles(client,
- player, nodepos, wasnode, features);
+ player, nodepos, n, features);
}
// Send event to trigger sound
- client->getEventManager()->put(new NodeDugEvent(nodepos, wasnode));
+ client->getEventManager()->put(new NodeDugEvent(nodepos, n));
}
if (runData.dig_time_complete < 100000.0) {
TimeTaker tt_update("Game::updateFrame()");
LocalPlayer *player = client->getEnv().getLocalPlayer();
+ /*
+ Frame time
+ */
+
+ client->getEnv().updateFrameTime(m_is_paused);
+
/*
Fog range
*/
float direct_brightness;
bool sunlight_seen;
- if (m_cache_enable_noclip && m_cache_enable_free_move) {
+ // When in noclip mode force same sky brightness as above ground so you
+ // can see properly
+ if (draw_control->allow_noclip && m_cache_enable_free_move &&
+ client->checkPrivilege("fly")) {
direct_brightness = time_brightness;
sunlight_seen = true;
} else {
);
}
+ /*
+ Damage camera tilt
+ */
+ if (player->hurt_tilt_timer > 0.0f) {
+ player->hurt_tilt_timer -= dtime * 6.0f;
+
+ if (player->hurt_tilt_timer < 0.0f)
+ player->hurt_tilt_strength = 0.0f;
+ }
+
+ /*
+ Update minimap pos and rotation
+ */
+ if (mapper && m_game_ui->m_flags.show_hud) {
+ mapper->setPos(floatToInt(player->getPosition(), BS));
+ mapper->setAngle(player->getYaw());
+ }
+
/*
Get chat messages from client
*/
changed much
*/
runData.update_draw_list_timer += dtime;
+ runData.touch_blocks_timer += dtime;
+
+ bool draw_list_updated = false;
float update_draw_list_delta = 0.2f;
v3f camera_direction = camera->getDirection();
if (runData.update_draw_list_timer >= update_draw_list_delta
|| runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
- || m_camera_offset_changed) {
-
+ || m_camera_offset_changed
+ || client->getEnv().getClientMap().needsUpdateDrawList()) {
runData.update_draw_list_timer = 0;
client->getEnv().getClientMap().updateDrawList();
runData.update_draw_list_last_cam_dir = camera_direction;
+ draw_list_updated = true;
+ }
+
+ if (runData.touch_blocks_timer > update_draw_list_delta && !draw_list_updated) {
+ client->getEnv().getClientMap().touchMapBlocks();
+ runData.touch_blocks_timer = 0;
}
if (RenderingEngine::get_shadow_renderer()) {
} while (false);
/*
- Drawing begins
+ ==================== Drawing begins ====================
*/
- const video::SColor &skycolor = sky->getSkyColor();
+ const video::SColor skycolor = sky->getSkyColor();
- TimeTaker tt_draw("Draw scene");
+ TimeTaker tt_draw("Draw scene", nullptr, PRECISION_MICRO);
driver->beginScene(true, true, skycolor);
bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
(camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
#ifdef HAVE_TOUCHSCREENGUI
- try {
- draw_crosshair = !g_settings->getBool("touchtarget");
- } catch (SettingNotFoundException) {
- }
+ if (isNoCrosshairAllowed())
+ draw_crosshair = false;
#endif
m_rendering_engine->draw_scene(skycolor, m_game_ui->m_flags.show_hud,
m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
}
/*
- Damage camera tilt
+ ==================== End scene ====================
*/
- if (player->hurt_tilt_timer > 0.0f) {
- player->hurt_tilt_timer -= dtime * 6.0f;
- if (player->hurt_tilt_timer < 0.0f)
- player->hurt_tilt_strength = 0.0f;
- }
-
- /*
- Update minimap pos and rotation
- */
- if (mapper && m_game_ui->m_flags.show_hud) {
- mapper->setPos(floatToInt(player->getPosition(), BS));
- mapper->setAngle(player->getYaw());
- }
-
- /*
- End scene
- */
- if (++m_reset_HW_buffer_counter > 500) {
- /*
- Periodically remove all mesh HW buffers.
-
- Work around for a quirk in Irrlicht where a HW buffer is only
- released after 20000 iterations (triggered from endScene()).
-
- Without this, all loaded but unused meshes will retain their HW
- buffers for at least 5 minutes, at which point looking up the HW buffers
- becomes a bottleneck and the framerate drops (as much as 30%).
-
- Tests showed that numbers between 50 and 1000 are good, so picked 500.
- There are no other public Irrlicht APIs that allow interacting with the
- HW buffers without tracking the status of every individual mesh.
-
- The HW buffers for _visible_ meshes will be reinitialized in the next frame.
- */
- infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl;
- driver->removeAllHardwareBuffers();
- m_reset_HW_buffer_counter = 0;
- }
driver->endScene();
stats->drawtime = tt_draw.stop(true);
- g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime);
- g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true));
+ g_profiler->graphAdd("Draw scene [us]", stats->drawtime);
+ g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true));
}
/* Log times and stuff for visualization */
float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
- float timeoftheday = fmod(getWickedTimeOfDay(in_timeofday) + 0.75f, 0.5f) + 0.25f;
+ float timeoftheday = getWickedTimeOfDay(in_timeofday);
+ bool is_day = timeoftheday > 0.25 && timeoftheday < 0.75;
+ bool is_shadow_visible = is_day ? sky->getSunVisible() : sky->getMoonVisible();
+ shadow->setShadowIntensity(is_shadow_visible ? client->getEnv().getLocalPlayer()->getLighting().shadow_intensity : 0.0f);
+
+ timeoftheday = fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
const float offset_constant = 10000.0f;
- v3f light(0.0f, 0.0f, -1.0f);
- light.rotateXZBy(90);
- light.rotateXYBy(timeoftheday * 360 - 90);
- light.rotateYZBy(sky->getSkyBodyOrbitTilt());
+ v3f light = is_day ? sky->getSunDirection() : sky->getMoonDirection();
v3f sun_pos = light * offset_constant;
Misc
****************************************************************************/
-/* On some computers framerate doesn't seem to be automatically limited
+void FpsControl::reset()
+{
+ last_time = porting::getTimeUs();
+}
+
+/*
+ * On some computers framerate doesn't seem to be automatically limited
*/
-inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
+void FpsControl::limit(IrrlichtDevice *device, f32 *dtime)
{
- // not using getRealTime is necessary for wine
- device->getTimer()->tick(); // Maker sure device time is up-to-date
- u32 time = device->getTimer()->getTime();
- u32 last_time = fps_timings->last_time;
+ const float fps_limit = (device->isWindowFocused() && !g_menumgr.pausesGame())
+ ? g_settings->getFloat("fps_max")
+ : g_settings->getFloat("fps_max_unfocused");
+ const u64 frametime_min = 1000000.0f / std::max(fps_limit, 1.0f);
- if (time > last_time) // Make sure time hasn't overflowed
- fps_timings->busy_time = time - last_time;
- else
- fps_timings->busy_time = 0;
+ u64 time = porting::getTimeUs();
- u32 frametime_min = 1000 / (
- device->isWindowFocused() && !g_menumgr.pausesGame()
- ? g_settings->getFloat("fps_max")
- : g_settings->getFloat("fps_max_unfocused"));
+ if (time > last_time) // Make sure time hasn't overflowed
+ busy_time = time - last_time;
+ else
+ busy_time = 0;
- if (fps_timings->busy_time < frametime_min) {
- fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
- device->sleep(fps_timings->sleep_time);
+ if (busy_time < frametime_min) {
+ sleep_time = frametime_min - busy_time;
+ if (sleep_time > 1000)
+ sleep_ms(sleep_time / 1000);
} else {
- fps_timings->sleep_time = 0;
+ sleep_time = 0;
}
- /* Get the new value of the device timer. Note that device->sleep() may
- * not sleep for the entire requested time as sleep may be interrupted and
- * therefore it is arguably more accurate to get the new time from the
- * device rather than calculating it by adding sleep_time to time.
- */
-
- device->getTimer()->tick(); // Update device timer
- time = device->getTimer()->getTime();
+ // Read the timer again to accurately determine how long we actually slept,
+ // rather than calculating it by adding sleep_time to time.
+ time = porting::getTimeUs();
- if (time > last_time) // Make sure last_time hasn't overflowed
- *dtime = (time - last_time) / 1000.0;
+ if (time > last_time) // Make sure last_time hasn't overflowed
+ *dtime = (time - last_time) / 1000000.0f;
else
*dtime = 0;
- fps_timings->last_time = time;
+ last_time = time;
}
void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds)
m_cache_enable_joysticks = g_settings->getBool("enable_joysticks");
m_cache_enable_particles = g_settings->getBool("enable_particles");
m_cache_enable_fog = g_settings->getBool("enable_fog");
- m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
- m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity");
- m_repeat_place_time = g_settings->getFloat("repeat_place_time");
+ m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity", 0.001f, 10.0f);
+ m_cache_joystick_frustum_sensitivity = std::max(g_settings->getFloat("joystick_frustum_sensitivity"), 0.001f);
+ m_repeat_place_time = g_settings->getFloat("repeat_place_time", 0.16f, 2.0);
m_cache_enable_noclip = g_settings->getBool("noclip");
m_cache_enable_free_move = g_settings->getBool("free_move");
#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
void Game::showPauseMenu()
{
-#ifdef __ANDROID__
+#ifdef HAVE_TOUCHSCREENGUI
static const std::string control_text = strgettext("Default Controls:\n"
"No menu visible:\n"
"- single tap: button activate\n"
if (simple_singleplayer_mode || address.empty()) {
static const std::string on = strgettext("On");
static const std::string off = strgettext("Off");
- const std::string &damage = g_settings->getBool("enable_damage") ? on : off;
- const std::string &creative = g_settings->getBool("creative_mode") ? on : off;
+ // Note: Status of enable_damage and creative_mode settings is intentionally
+ // NOT shown here because the game might roll its own damage system and/or do
+ // a per-player Creative Mode, in which case writing it here would mislead.
+ bool damage = g_settings->getBool("enable_damage");
const std::string &announced = g_settings->getBool("server_announce") ? on : off;
- os << strgettext("- Damage: ") << damage << "\n"
- << strgettext("- Creative Mode: ") << creative << "\n";
if (!simple_singleplayer_mode) {
- const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
- //~ PvP = Player versus Player
- os << strgettext("- PvP: ") << pvp << "\n"
- << strgettext("- Public: ") << announced << "\n";
+ if (damage) {
+ const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
+ //~ PvP = Player versus Player
+ os << strgettext("- PvP: ") << pvp << "\n";
+ }
+ os << strgettext("- Public: ") << announced << "\n";
std::string server_name = g_settings->get("server_name");
str_formspec_escape(server_name);
if (announced == on && !server_name.empty())
GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(),
&input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound);
formspec->setFocus("btn_continue");
+ // game will be paused in next step, if in singleplayer (see m_is_paused)
formspec->doPause = true;
-
- if (simple_singleplayer_mode)
- pauseAnimation();
}
/****************************************************************************/
}
} catch (SerializationError &e) {
- error_message = std::string("A serialization error occurred:\n")
- + e.what() + "\n\nThe server is probably "
- " running a different version of " PROJECT_NAME_C ".";
+ const std::string ver_err = fmtgettext("The server is probably running a different version of %s.", PROJECT_NAME_C);
+ error_message = strgettext("A serialization error occurred:") +"\n"
+ + e.what() + "\n\n" + ver_err;
errorstream << error_message << std::endl;
} catch (ServerError &e) {
error_message = e.what();
errorstream << "ServerError: " << error_message << std::endl;
} catch (ModError &e) {
+ // DO NOT TRANSLATE the `ModError`, it's used by ui.lua
error_message = std::string("ModError: ") + e.what() +
strgettext("\nCheck debug.txt for details.");
errorstream << error_message << std::endl;