Index: ps/trunk/binaries/data/config/default.cfg
===================================================================
--- ps/trunk/binaries/data/config/default.cfg (revision 16926)
+++ ps/trunk/binaries/data/config/default.cfg (revision 16927)
@@ -1,406 +1,409 @@
; Global Configuration Settings
;
; **************************************************************
; * DO NOT EDIT THIS FILE if you want personal customisations: *
; * create a text file called "local.cfg" instead, and copy *
; * the lines from this file that you want to change. *
; * *
; * On Linux, create: *
; * $XDG_CONFIG_HOME/0ad/config/local.cfg *
; * (Note: $XDG_CONFIG_HOME defaults to ~/.config) *
; * *
; * On OS X, create: *
; * ~/Library/Application\ Support/0ad/config/local.cfg *
; * *
; * On Windows, create: *
; * %appdata%\0ad\config\local.cfg *
; * *
; **************************************************************
; Enable/disable windowed mode by default. (Use Alt+Enter to toggle in the game.)
windowed = false
; Show detailed tooltips (Unit stats)
showdetailedtooltips = false
; Enable/disable the splashscreen
splashscreendisable = false
; Splashscreen version (date of last modification). By default, 0 to force splashscreen to appear at first launch.
splashscreenversion = 0
; Pause the game on window focus loss (Only applicable to single player mode)
pauseonfocusloss = true
; Persist settings after leaving the game setup screen
persistmatchsettings = true
; Default player name to use in multiplayer
; playername = "anonymous"
; Default server name or IP to use in multiplayer
multiplayerserver = "127.0.0.1"
; Force a particular resolution. (If these are 0, the default is
; to keep the current desktop resolution in fullscreen mode or to
; use 1024x768 in windowed mode.)
xres = 0
yres = 0
; Force a non-standard bit depth (if 0 then use the current desktop bit depth)
bpp = 0
; Preferred display (for multidisplay setups, only works with SDL 2.0)
display = 0
; Emulate right-click with Ctrl+Click on Mac mice
macmouse = false
; System settings:
; if false, actors won't be rendered but anything entity will be.
renderactors = true
waterugly=false; Force usage of the fixed pipeline water. This is faster, but really, really ugly.
waterfancyeffects = false
waterrealdepth = true
waterrefraction = true
waterreflection = true
shadowsonwater = false
shadows = true
shadowpcf = true
vsync = false
particles = true
silhouettes = true
showsky = false
nos3tc = false
noautomipmap = true
novbo = false
noframebufferobject = false
; Disable hardware cursors
nohwcursor = false
; Linux only: Set the driconf force_s3tc_enable option at startup,
; for compressed texture support
force_s3tc_enable = true
; Specify the render path. This can be one of:
; default Automatically select one of the below, depending on system capabilities
; fixed Only use OpenGL fixed function pipeline
; shader Use vertex/fragment shaders for transform and lighting where possible
; Using 'fixed' instead of 'default' may work around some graphics-related problems,
; but will reduce performance and features when a modern graphics card is available.
renderpath = default
;;;;; EXPERIMENTAL ;;;;;
; Prefer GLSL shaders over ARB shaders (not recommended). REQUIRES gentangents=true.
preferglsl = false
; Generate tangents for normal and parallax mapping. REQUIRES preferglsl=true.
gentangents = false
; Experimental probably-non-working GPU skinning support; requires preferglsl; use at own risk
gpuskinning = false
; Use smooth LOS interpolation
smoothlos = false
; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
postproc = false
; Quality level of shader effects (set to 10 to display effects)
materialmgr.quality = 0.0
; Maximum distance to display parallax effect. Set to 0 to disable parallax.
materialmgr.PARALLAX_DIST.max = 150
; Maximum distance to display high quality parallax effect.
materialmgr.PARALLAX_HQ_DIST.max = 75
; Maximum distance to display very high quality parallax effect. Set to 30 to enable.
materialmgr.PARALLAX_VHQ_DIST.max = 0
;;;;;;;;;;;;;;;;;;;;;;;;
; Replace alpha-blending with alpha-testing, for performance experiments
forcealphatest = false
; Color of the sky (in "r g b" format)
skycolor = "0 0 0"
[hotkey]
; Each one of the specified keys will trigger the action on the left
; for multiple-key combinations, separate keys with '+'.
; See keys.txt for the list of key names.
; > SYSTEM SETTINGS
exit = "Alt+F4", "Ctrl+Break", "Super+Q" ; Exit to desktop
cancel = Escape ; Close or cancel the current dialog box/popup
leave = Escape ; End current game or Exit
confirm = Return ; Confirm the current command
pause = Pause ; Pause/unpause game
screenshot = F2 ; Take PNG screenshot
bigscreenshot = "Shift+F2" ; Take large BMP screenshot
togglefullscreen = "Alt+Return" ; Toggle fullscreen/windowed mode
screenshot.watermark = "Alt+K" ; Toggle product/company watermark for official screenshots
wireframe = "Alt+W" ; Toggle wireframe mode
silhouettes = "Alt+S" ; Toggle unit silhouettes
showsky = "Alt+Z" ; Toggle sky
; > CLIPBOARD CONTROLS
copy = "Ctrl+C" ; Copy to clipboard
paste = "Ctrl+V" ; Paste from clipboard
cut = "Ctrl+X" ; Cut selected text and copy to the clipboard
; > CONSOLE SETTINGS
console.toggle = BackQuote, F9 ; Open/close console
; > OVERLAY KEYS
fps.toggle = "Alt+F" ; Toggle frame counter
realtime.toggle = "Alt+T" ; Toggle current display of computer time
session.devcommands.toggle = "Alt+D" ; Toggle developer commands panel
session.gui.toggle = "Alt+G" ; Toggle visibility of session GUI
menu.toggle = "F10" ; Toggle in-game menu
timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter
session.showstatusbars = Tab ; Toggle display of status bars
session.highlightguarding = PgDn ; Toggle highlight of guarding units
session.highlightguarded = PgUp ; Toggle highlight of guarded units
; > HOTKEYS ONLY
chat = Return ; Toggle chat window
teamchat = "T" ; Toggle chat window in team chat mode
; > QUICKSAVE
quicksave = "Shift+F5"
quickload = "Shift+F8"
[hotkey.camera]
reset = "R" ; Reset camera rotation to default.
follow = "F" ; Follow the first unit in the selection
zoom.in = Plus, Equals, NumPlus ; Zoom camera in (continuous control)
zoom.out = Minus, NumMinus ; Zoom camera out (continuous control)
zoom.wheel.in = WheelUp ; Zoom camera in (stepped control)
zoom.wheel.out = WheelDown ; Zoom camera out (stepped control)
rotate.up = "Ctrl+UpArrow", "Ctrl+W" ; Rotate camera to look upwards
rotate.down = "Ctrl+DownArrow", "Ctrl+S" ; Rotate camera to look downwards
rotate.cw = "Ctrl+LeftArrow", "Ctrl+A", Q ; Rotate camera clockwise around terrain
rotate.ccw = "Ctrl+RightArrow", "Ctrl+D", E ; Rotate camera anticlockwise around terrain
rotate.wheel.cw = "Shift+WheelUp", MouseX1 ; Rotate camera clockwise around terrain (stepped control)
rotate.wheel.ccw = "Shift+WheelDown", MouseX2 ; Rotate camera anticlockwise around terrain (stepped control)
pan = MouseMiddle ; Enable scrolling by moving mouse
left = A, LeftArrow ; Scroll or rotate left
right = D, RightArrow ; Scroll or rotate right
up = W, UpArrow ; Scroll or rotate up/forwards
down = S, DownArrow ; Scroll or rotate down/backwards
scroll.speed.increase = "Ctrl+Shift+S" ; Increase scroll speed
scroll.speed.decrease = "Ctrl+Alt+S" ; Decrease scroll speed
rotate.speed.increase = "Ctrl+Shift+R" ; Increase rotation speed
rotate.speed.decrease = "Ctrl+Alt+R" ; Decrease rotation speed
zoom.speed.increase = "Ctrl+Shift+Z" ; Increase zoom speed
zoom.speed.decrease = "Ctrl+Alt+Z" ; Decrease zoom speed
[hotkey.camera.jump]
1 = F5 ; Jump to position N
2 = F6
3 = F7
4 = F8
;5 =
;6 =
;7 =
;8 =
;9 =
;10 =
[hotkey.camera.jump.set]
1 = "Ctrl+F5" ; Set jump position N
2 = "Ctrl+F6"
3 = "Ctrl+F7"
4 = "Ctrl+F8"
;5 =
;6 =
;7 =
;8 =
;9 =
;10 =
[hotkey.profile]
toggle = "F11" ; Enable/disable real-time profiler
save = "Shift+F11" ; Save current profiler data to logs/profile.txt
[hotkey.profile2]
toggle = "Ctrl+F11" ; Enable/disable HTTP/GPU modes for new profiler
[hotkey.selection]
add = Shift ; Add units to selection
milonly = Alt ; Add only military units to selection
idleonly = "I" ; Select only idle units
remove = Ctrl ; Remove units from selection
cancel = Esc ; Un-select all units and cancel building placement
idleworker = Period ; Select next idle worker
idlewarrior = ForwardSlash ; Select next idle warrior
offscreen = Alt ; Include offscreen units in selection
[hotkey.selection.group.add]
0 = "Shift+0"
1 = "Shift+1"
2 = "Shift+2"
3 = "Shift+3"
4 = "Shift+4"
5 = "Shift+5"
6 = "Shift+6"
7 = "Shift+7"
8 = "Shift+8"
9 = "Shift+9"
[hotkey.selection.group.save]
0 = "Ctrl+0"
1 = "Ctrl+1"
2 = "Ctrl+2"
3 = "Ctrl+3"
4 = "Ctrl+4"
5 = "Ctrl+5"
6 = "Ctrl+6"
7 = "Ctrl+7"
8 = "Ctrl+8"
9 = "Ctrl+9"
[hotkey.selection.group.select]
0 = 0
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
7 = 7
8 = 8
9 = 9
[hotkey.session]
kill = Delete ; Destroy selected units
stop = "H" ; Stop the current action
attack = "Ctrl+Alt" ; Modifier to force attack instead of another action
attackmove = Ctrl ; Modifier to attackmove when clicking on a point
attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point (should contain the attackmove keys)
garrison = Ctrl ; Modifier to garrison when clicking on building
autorallypoint = Ctrl ; Modifier to set the rally point on the building itself
guard = "G" ; Modifier to escort/guard when clicking on unit/building
queue = Shift ; Modifier to queue unit orders instead of replacing
batchtrain = Shift ; Modifier to train units in batches
massbarter = Shift ; Modifier to barter bunch of resources
masstribute = Shift ; Modifier to tribute bunch of resources
fulltradeswap = Shift ; Modifier to put the desired trade resource to 100%
unloadtype = Shift ; Modifier to unload all units of type
deselectgroup = Ctrl ; Modifier to deselect units when clicking group icon, instead of selecting
rotate.cw = RightBracket ; Rotate building placement preview clockwise
rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise
[hotkey.session.savedgames]
delete = Delete ; Delete the selected saved game asking confirmation
noConfirmation = Shift ; Do not ask confirmation when deleting a game
[hotkey.session.queueunit] ; > UNIT TRAINING
1 = "Z" ; add first unit type to queue
2 = "X" ; add second unit type to queue
3 = "C" ; add third unit type to queue
4 = "V" ; add fourth unit type to queue
5 = "B" ; add fivth unit type to queue
6 = "N" ; add sixth unit type to queue
7 = "M" ; add seventh unit type to queue
8 = Comma ; add eighth unit type to queue
[hotkey.session.timewarp]
fastforward = Space ; If timewarp mode enabled, speed up the game
rewind = Backspace ; If timewarp mode enabled, go back to earlier point in the game
[hotkey.text] ; > GUI TEXTBOX HOTKEYS
delete.left = "Ctrl+Backspace" ; Delete word to the left of cursor
delete.right = "Ctrl+Del" ; Delete word to the right of cursor
move.left = "Ctrl+LeftArrow" ; Move cursor to start of word to the left of cursor
move.right = "Ctrl+RightArrow" ; Move cursor to start of word to the right of cursor
[gui]
cursorblinkrate = 0.5 ; Cursor blink rate in seconds (0.0 to disable blinking)
scale = 1.0 ; GUI scaling factor, for improved compatibility with 4K displays
+[gui.menu]
+limitfps = true ; Limit FPS in the menus and loading screen
+
[gui.session]
attacknotificationmessage = true ; Show attack notification messages
camerajump.threshold = 40 ; How close do we have to be to the actual location in order to jump back to the previous one?
timeelapsedcounter = false ; Show the game duration in the top right corner
[gui.session.minimap]
blinkduration = 1.7 ; The blink duration while pinging
pingduration = 50.0 ; The duration for which an entity will be pinged after an attack notification
[joystick] ; EXPERIMENTAL: joystick/gamepad settings
enable = false
deadzone = 8192
[joystick.camera]
pan.x = 0
pan.y = 1
rotate.x = 3
rotate.y = 2
zoom.in = 5
zoom.out = 4
[jsdebugger]
; The Debugger is currently broken and can't be enabled until the SpiderMonkey upgrade is done and the debugger is updated for the new API.
enable = false ; Enable Javascript debugging.
[lobby]
chattimestamp = false ; Show time chat message was posted
history = 0 ; Number of past messages to display on join
room = "arena19" ; Default MUC room to join
server = "lobby.wildfiregames.com" ; Address of lobby server
xpartamupp = "wfgbot19" ; Name of the server-side xmpp client that manage games
[mod]
enabledmods = "mod public"
[overlay]
fps = "false" ; Show frames per second in top right corner
realtime = "false" ; Show current system time in top right corner
[profiler2]
autoenable = false ; Enable HTTP server output at startup (default off for security/performance)
script.enable = false ; Enable Javascript profiling. Needs to be set before startup and can't be changed later. (default off for performance)
gpu.arb.enable = true ; Allow GL_ARB_timer_query timing mode when available
gpu.ext.enable = true ; Allow GL_EXT_timer_query timing mode when available
gpu.intel.enable = true ; Allow GL_INTEL_performance_queries timing mode when available
[sound]
mastergain = 0.9
musicgain = 0.2
ambientgain = 0.6
actiongain = 0.7
uigain = 0.7
[tinygettext]
debug = false ; Print error messages each time a translation for an English string is not found.
[userreport] ; Opt-in online user reporting system
url = "http://feedback.wildfiregames.com/report/upload/v1/"
[view] ; Camera control settings
scroll.speed = 120.0
scroll.speed.modifier = 1.05 ; Multiplier for changing scroll speed
rotate.x.speed = 1.2
rotate.x.min = 28.0
rotate.x.max = 60.0
rotate.x.default = 35.0
rotate.y.speed = 2.0
rotate.y.speed.wheel = 0.45
rotate.y.default = 0.0
rotate.speed.modifier = 1.05 ; Multiplier for changing rotation speed
drag.speed = 0.5
zoom.speed = 256.0
zoom.speed.wheel = 32.0
zoom.min = 50.0
zoom.max = 200.0
zoom.default = 120.0
zoom.speed.modifier = 1.05 ; Multiplier for changing zoom speed
pos.smoothness = 0.1
zoom.smoothness = 0.4
rotate.x.smoothness = 0.5
rotate.y.smoothness = 0.3
near = 2.0 ; Near plane distance
far = 4096.0 ; Far plane distance
fov = 45.0 ; Field of view (degrees), lower is narrow, higher is wide
height.smoothness = 0.5
height.min = 16
Index: ps/trunk/binaries/data/mods/public/gui/options/options.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 16926)
+++ ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 16927)
@@ -1,234 +1,235 @@
var g_hasCallback = false;
/**
* This array holds the data to populate the general section with.
* Data is in the form [Title, Tooltip, {ActionType:Action}, InputType].
*/
var options = {
"generalSetting":
[
[translate("Windowed Mode"), translate("Start 0 A.D. in a window"), {"config":"windowed"}, "boolean"],
[translate("Background Pause"), translate("Pause single player games when window loses focus"), {"config":"pauseonfocusloss"}, "boolean"],
[translate("Disable Welcome Screen"), translate("If you disable this screen completely, you may miss important announcements.\nYou can still launch it using the main menu."), {"config":"splashscreendisable"}, "boolean"],
[translate("Detailed Tooltips"), translate("Show detailed tooltips for trainable units in unit-producing buildings."), {"config":"showdetailedtooltips"}, "boolean"],
[translate("FPS Overlay"), translate("Show frames per second in top right corner."), {"config":"overlay.fps"}, "boolean"],
[translate("Realtime Overlay"), translate("Show current system time in top right corner."), {"config":"overlay.realtime"}, "boolean"],
[translate("Gametime Overlay"), translate("Show current simulation time in top right corner."), {"config":"gui.session.timeelapsedcounter"}, "boolean"],
[translate("Ceasefire Time Overlay"), translate("Always show the remaining ceasefire time."), {"config":"gui.session.ceasefirecounter"}, "boolean"],
- [translate("Persist match settings"), translate("Save and restore match settings for quick reuse when hosting another game"), {"config":"persistmatchsettings"}, "boolean"],
+ [translate("Persist Match Settings"), translate("Save and restore match settings for quick reuse when hosting another game"), {"config":"persistmatchsettings"}, "boolean"],
],
"graphicsSetting":
[
[translate("Prefer GLSL"), translate("Use OpenGL 2.0 shaders (recommended)"), {"renderer":"PreferGLSL", "config":"preferglsl"}, "boolean"],
[translate("Post Processing"), translate("Use screen-space postprocessing filters (HDR, Bloom, DOF, etc)"), {"renderer":"Postproc", "config":"postproc"}, "boolean"],
[translate("Shadows"), translate("Enable shadows"), {"renderer":"Shadows", "config":"shadows"}, "boolean"],
[translate("Particles"), translate("Enable particles"), {"renderer":"Particles", "config":"particles"}, "boolean"],
[translate("Show Sky"), translate("Render Sky"), {"renderer":"ShowSky", "config":"showsky"}, "boolean"],
[translate("Smooth LOS"), translate("Lift darkness and fog-of-war smoothly"), {"renderer":"SmoothLOS", "config":"smoothlos"}, "boolean"],
[translate("Unit Silhouettes"), translate("Show outlines of units behind buildings"), {"renderer":"Silhouettes", "config":"silhouettes"}, "boolean"],
[translate("Shadow Filtering"), translate("Smooth shadows"), {"renderer":"ShadowPCF", "config":"shadowpcf"}, "boolean"],
[translate("Fast & Ugly Water"), translate("Use the lowest settings possible to render water. This makes other settings irrelevant."), {"renderer":"WaterUgly", "config":"waterugly"}, "boolean"],
[translate("HQ Water Effects"), translate("Use higher-quality effects for water, rendering coastal waves, shore foam, and ships trails."), {"renderer":"WaterFancyEffects", "config":"waterfancyeffects"}, "boolean"],
[translate("Real Water Depth"), translate("Use actual water depth in rendering calculations"), {"renderer":"WaterRealDepth", "config":"waterrealdepth"}, "boolean"],
[translate("Water Reflections"), translate("Allow water to reflect a mirror image"), {"renderer":"WaterReflection", "config":"waterreflection"}, "boolean"],
[translate("Water Refraction"), translate("Use a real water refraction map and not transparency"), {"renderer":"WaterRefraction", "config":"waterrefraction"}, "boolean"],
[translate("Shadows on Water"), translate("Cast shadows on water"), {"renderer":"WaterShadows", "config":"watershadows"}, "boolean"],
[translate("VSync"), translate("Run vertical sync to fix screen tearing. REQUIRES GAME RESTART"), {"config":"vsync"}, "boolean"],
+ [translate("Limit FPS in Menus"), translate("Limit FPS to 50 in all menus, to save power."), {"config":"gui.menu.limitfps"}, "boolean"],
],
"soundSetting":
[
[translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"],
[translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("Ambient Gain"), translate("In game ambient sound gain"), {"config":"sound.ambientgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("Action Gain"), translate("In game unit action sound gain"), {"config":"sound.actiongain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("UI Gain"), translate("UI sound gain"), {"config":"sound.uigain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
],
"lobbySetting":
[
[translate("Chat Backlog"), translate("Number of backlogged messages to load when joining the lobby"), {"config":"lobby.history"}, "number"],
[translate("Chat Timestamp"), translate("Show time that messages are posted in the lobby chat"), {"config":"lobby.chattimestamp"}, "boolean"],
],
};
function init(data)
{
if (data && data.callback)
g_hasCallback = true;
// WARNING: We assume a strict formatting of the XML and do minimal checking.
for each (var prefix in Object.keys(options))
{
var lastSize;
for (var i = 0; i < options[prefix].length; i++)
{
var body = Engine.GetGUIObjectByName(prefix + "[" + i + "]");
var label = Engine.GetGUIObjectByName(prefix + "Label[" + i + "]");
// Setup control.
setupControl(options[prefix][i], i, prefix);
// Setup label.
label.caption = options[prefix][i][0];
label.tooltip = options[prefix][i][1];
// Move each element to the correct place.
if (i > 0)
{
var newSize = new GUISize();
newSize.left = lastSize.left;
newSize.rright = lastSize.rright;
newSize.top = lastSize.bottom;
newSize.bottom = newSize.top + 25;
body.size = newSize;
lastSize = newSize;
}
else
{
lastSize = body.size;
}
// Show element.
body.hidden = false;
}
}
}
/**
* Setup the apropriate control for a given option.
*
* @param option Structure containing the data to setup an option.
* @param prefix Prefix to use when accessing control, for example "generalSetting" when the tickbox name is generalSettingTickbox[i].
*/
function setupControl(option, i, prefix)
{
switch (option[3])
{
case "boolean":
// More space for the label
let text = Engine.GetGUIObjectByName(prefix + "Label[" + i + "]");
let size = text.size;
size.rright = 87;
text.size = size;
var control = Engine.GetGUIObjectByName(prefix + "Tickbox[" + i + "]");
var checked;
var onPress = function(){};
// Different option action load and save differently, so this switch is needed.
for each (var action in Object.keys(option[2]))
{
switch (action)
{
case "config":
// Load initial value if not yet loaded.
if (!checked || typeof checked != "boolean")
checked = Engine.ConfigDB_GetValue("user", option[2][action]) === "true" ? true : false;
// Hacky macro to create the callback.
var callback = function(key)
{
return function()
Engine.ConfigDB_CreateValue("user", key, String(this.checked));
}(option[2][action]);
// Merge the new callback with any existing callbacks.
onPress = mergeFunctions(callback, onPress);
// TODO: Remove this once GLSL works without GenTangents (#2506)
if (option[2][action] == "preferglsl")
{
callback = function()
Engine.ConfigDB_CreateValue("user", "gentangents", String(this.checked));
onPress = mergeFunctions(callback, onPress);
}
break;
case "renderer":
// Load initial value if not yet loaded.
if (!checked || typeof checked != "boolean")
checked = eval("Engine.Renderer_Get" + option[2][action] + "Enabled()");
// Hacky macro to create the callback.
var callback = function(key)
{
return function()
eval("Engine.Renderer_Set" + key + "Enabled(" + this.checked + ")");
}(option[2][action]);
// Merge the new callback with any existing callbacks.
onPress = mergeFunctions(callback, onPress);
// TODO: Remove this once GLSL works without GenTangents (#2506)
if (option[2][action] == "PreferGLSL")
{
callback = function()
eval("Engine.Renderer_SetGenTangentsEnabled(" + this.checked + ")");
onPress = mergeFunctions(callback, onPress);
}
break;
case "function":
// This allows for doing low-level actions, like hiding/showing UI elements.
onPress = mergeFunctions(eval("function(){" + option[2][action] + "}"), onPress);
break;
default:
warn("Unknown option source type '" + action + "'");
}
}
// Load final data to the control element.
control.checked = checked;
control.onPress = onPress;
break;
case "number":
// TODO: Slider
case "string":
var control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
var caption;
var onPress = function(){};
for each (var action in Object.keys(option[2]))
{
switch (action)
{
case "config":
// Load initial value if not yet loaded.
if (!checked || typeof checked != boolean)
caption = Engine.ConfigDB_GetValue("user", option[2][action]);;
// Hacky macro to create the callback.
var callback = function(key)
{
return function()
Engine.ConfigDB_CreateValue("user", key, String(this.caption));
}(option[2][action]);
// Merge the new callback with any existing callbacks.
onPress = mergeFunctions(callback, onPress);
break;
case "function":
// This allows for doing low-level actions, like hiding/showing UI elements.
onPress = mergeFunctions(function(){eval(option[2][action])}, onPress);
break;
default:
warn("Unknown option source type '" + action + "'");
}
}
control.caption = caption;
control.onPress = onPress;
break;
default:
warn("Unknown option type '" + options[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
var control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
break;
}
control.hidden = false;
control.tooltip = option[1];
return control;
}
/**
* Merge two functions which don't expect arguments.
*
* @return Merged function.
*/
function mergeFunctions(function1, function2)
{
return function()
{
function1.apply(this);
function2.apply(this);
};
}
/**
* Close GUI page and call callbacks if they exist.
**/
function closePage()
{
if (g_hasCallback)
Engine.PopGuiPageCB();
else
Engine.PopGuiPage();
}
Index: ps/trunk/source/main.cpp
===================================================================
--- ps/trunk/source/main.cpp (revision 16926)
+++ ps/trunk/source/main.cpp (revision 16927)
@@ -1,573 +1,585 @@
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
This module drives the game when running without Atlas (our integrated
map editor). It receives input and OS messages via SDL and feeds them
into the input dispatcher, where they are passed on to the game GUI and
simulation.
It also contains main(), which either runs the above controller or
that of Atlas depending on commandline parameters.
*/
// not for any PCH effort, but instead for the (common) definitions
// included there.
#define MINIMAL_PCH 2
#include "lib/precompiled.h"
#include "lib/debug.h"
#include "lib/status.h"
#include "lib/secure_crt.h"
#include "lib/frequency_filter.h"
#include "lib/input.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "lib/external_libraries/libsdl.h"
#include "ps/ArchiveBuilder.h"
#include "ps/CConsole.h"
#include "ps/CLogger.h"
+#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
#include "ps/Game.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Loader.h"
#include "ps/Profile.h"
#include "ps/Profiler2.h"
#include "ps/Pyrogenesis.h"
#include "ps/Replay.h"
#include "ps/TouchInput.h"
#include "ps/UserReport.h"
#include "ps/Util.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/GameSetup/Atlas.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/CmdLineArgs.h"
#include "ps/GameSetup/Paths.h"
#include "ps/XML/Xeromyces.h"
#include "network/NetClient.h"
#include "network/NetServer.h"
#include "network/NetSession.h"
#include "lobby/IXmppClient.h"
#include "graphics/Camera.h"
#include "graphics/GameView.h"
#include "graphics/TextureManager.h"
#include "gui/GUIManager.h"
#include "renderer/Renderer.h"
#include "simulation2/Simulation2.h"
#if OS_UNIX
#include // geteuid
#endif // OS_UNIX
extern bool g_GameRestarted;
void kill_mainloop();
// to avoid redundant and/or recursive resizing, we save the new
// size after VIDEORESIZE messages and only update the video mode
// once per frame.
// these values are the latest resize message, and reset to 0 once we've
// updated the video mode
static int g_ResizedW;
static int g_ResizedH;
// main app message handler
static InReaction MainInputHandler(const SDL_Event_* ev)
{
switch(ev->ev.type)
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_WINDOWEVENT:
switch(ev->ev.window.event)
{
case SDL_WINDOWEVENT_ENTER:
RenderCursor(true);
break;
case SDL_WINDOWEVENT_LEAVE:
RenderCursor(false);
break;
case SDL_WINDOWEVENT_RESIZED:
g_ResizedW = ev->ev.window.data1;
g_ResizedH = ev->ev.window.data2;
break;
case SDL_WINDOWEVENT_MOVED:
g_VideoMode.UpdatePosition(ev->ev.window.data1, ev->ev.window.data2);
}
break;
#else
case SDL_ACTIVEEVENT:
if (ev->ev.active.state & SDL_APPMOUSEFOCUS)
{
// Tell renderer not to render cursor if mouse focus is lost
// this restores system cursor, until/if focus is regained
if (!ev->ev.active.gain)
RenderCursor(false);
else
RenderCursor(true);
}
break;
case SDL_VIDEORESIZE:
g_ResizedW = ev->ev.resize.w;
g_ResizedH = ev->ev.resize.h;
break;
#endif
case SDL_QUIT:
kill_mainloop();
break;
case SDL_HOTKEYDOWN:
std::string hotkey = static_cast(ev->ev.user.data1);
if (hotkey == "exit")
{
kill_mainloop();
return IN_HANDLED;
}
else if (hotkey == "screenshot")
{
WriteScreenshot(L".png");
return IN_HANDLED;
}
else if (hotkey == "bigscreenshot")
{
WriteBigScreenshot(L".bmp", 10);
return IN_HANDLED;
}
else if (hotkey == "togglefullscreen")
{
g_VideoMode.ToggleFullscreen();
return IN_HANDLED;
}
else if (hotkey == "profile2.toggle")
{
g_Profiler2.Toggle();
return IN_HANDLED;
}
break;
}
return IN_PASS;
}
// dispatch all pending events to the various receivers.
static void PumpEvents()
{
JSContext* cx = g_GUI->GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
PROFILE3("dispatch events");
SDL_Event_ ev;
while (in_poll_event(&ev))
{
PROFILE2("event");
if (g_GUI)
{
JS::RootedValue tmpVal(cx);
ScriptInterface::ToJSVal(cx, &tmpVal, ev);
std::string data = g_GUI->GetScriptInterface()->StringifyJSON(&tmpVal);
PROFILE2_ATTR("%s", data.c_str());
}
in_dispatch_event(&ev);
}
g_TouchInput.Frame();
}
static int ProgressiveLoad()
{
PROFILE3("progressive load");
wchar_t description[100];
int progress_percent;
try
{
Status ret = LDR_ProgressiveLoad(10e-3, description, ARRAY_SIZE(description), &progress_percent);
switch(ret)
{
// no load active => no-op (skip code below)
case INFO::OK:
return 0;
// current task didn't complete. we only care about this insofar as the
// load process is therefore not yet finished.
case ERR::TIMED_OUT:
break;
// just finished loading
case INFO::ALL_COMPLETE:
g_Game->ReallyStartGame();
wcscpy_s(description, ARRAY_SIZE(description), L"Game is starting..");
// LDR_ProgressiveLoad returns L""; set to valid text to
// avoid problems in converting to JSString
break;
// error!
default:
WARN_RETURN_STATUS_IF_ERR(ret);
// can't do this above due to legit ERR::TIMED_OUT
break;
}
}
catch (PSERROR_Game_World_MapLoadFailed& e)
{
// Map loading failed
// Call script function to do the actual work
// (delete game data, switch GUI page, show error, etc.)
CancelLoad(CStr(e.what()).FromUTF8());
}
GUI_DisplayLoadProgress(progress_percent, description);
return 0;
}
static void RendererIncrementalLoad()
{
PROFILE3("renderer incremental load");
const double maxTime = 0.1f;
double startTime = timer_Time();
bool more;
do {
more = g_Renderer.GetTextureManager().MakeProgress();
}
while (more && timer_Time() - startTime < maxTime);
}
static bool quit = false; // break out of main loop
static void Frame()
{
g_Profiler2.RecordFrameStart();
PROFILE2("frame");
g_Profiler2.IncrementFrameNumber();
PROFILE2_ATTR("%d", g_Profiler2.GetFrameNumber());
ogl_WarnIfError();
// get elapsed time
const double time = timer_Time();
g_frequencyFilter->Update(time);
// .. old method - "exact" but contains jumps
#if 0
static double last_time;
const double time = timer_Time();
const float TimeSinceLastFrame = (float)(time-last_time);
last_time = time;
ONCE(return); // first call: set last_time and return
// .. new method - filtered and more smooth, but errors may accumulate
#else
const float realTimeSinceLastFrame = 1.0 / g_frequencyFilter->SmoothedFrequency();
#endif
ENSURE(realTimeSinceLastFrame > 0.0f);
// decide if update/render is necessary
bool need_render = !g_app_minimized;
bool need_update = true;
// If we are not running a multiplayer game, disable updates when the game is
// minimized or out of focus and relinquish the CPU a bit, in order to make
// debugging easier.
if(g_PauseOnFocusLoss && !g_NetClient && !g_app_has_focus)
{
PROFILE3("non-focus delay");
need_update = false;
// don't use SDL_WaitEvent: don't want the main loop to freeze until app focus is restored
SDL_Delay(10);
}
- // TODO: throttling: limit update and render frequency to the minimum.
- // this is mostly relevant for "inactive" state, so that other windows
- // get enough CPU time, but it's always nice for power+thermal management.
+ // Throttling: limit update and render frequency to the minimum to 50 FPS
+ // in the "inactive" state, so that other windows get enough CPU time,
+ // (and it's always nice for power+thermal management).
+ // TODO: when the game performance is high enough, implementing a limit for
+ // in-game framerate might be sensible.
+ const float maxFPSMenu = 50.0;
+ bool limit_fps = false;
+ CFG_GET_VAL("gui.menu.limitfps", limit_fps);
+ if (limit_fps && (!g_Game || !g_Game->IsGameStarted()))
+ {
+ float remainingFrameTime = (1000.0 / maxFPSMenu) - realTimeSinceLastFrame;
+ if (remainingFrameTime > 0)
+ SDL_Delay(remainingFrameTime);
+ }
// this scans for changed files/directories and reloads them, thus
// allowing hotloading (changes are immediately assimilated in-game).
ReloadChangedFiles();
ProgressiveLoad();
RendererIncrementalLoad();
PumpEvents();
// if the user quit by closing the window, the GL context will be broken and
// may crash when we call Render() on some drivers, so leave this loop
// before rendering
if (quit)
return;
// respond to pumped resize events
if (g_ResizedW || g_ResizedH)
{
g_VideoMode.ResizeWindow(g_ResizedW, g_ResizedH);
g_ResizedW = g_ResizedH = 0;
}
if (g_NetClient)
g_NetClient->Poll();
ogl_WarnIfError();
g_GUI->TickObjects();
ogl_WarnIfError();
if (g_Game && g_Game->IsGameStarted() && need_update)
{
g_Game->Update(realTimeSinceLastFrame);
g_Game->GetView()->Update(float(realTimeSinceLastFrame));
}
// Immediately flush any messages produced by simulation code
if (g_NetClient)
g_NetClient->Flush();
// Keep us connected to any XMPP servers
if (g_XmppClient)
g_XmppClient->recv();
g_UserReporter.Update();
g_Console->Update(realTimeSinceLastFrame);
ogl_WarnIfError();
if(need_render)
{
Render();
PROFILE3("swap buffers");
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_GL_SwapWindow(g_VideoMode.GetWindow());
#else
SDL_GL_SwapBuffers();
#endif
}
ogl_WarnIfError();
g_Profiler.Frame();
g_GameRestarted = false;
}
static void MainControllerInit()
{
// add additional input handlers only needed by this controller:
// must be registered after gui_handler. Should mayhap even be last.
in_add_handler(MainInputHandler);
}
static void MainControllerShutdown()
{
in_reset_handlers();
}
// stop the main loop and trigger orderly shutdown. called from several
// places: the event handler (SDL_QUIT and hotkey) and JS exitProgram.
void kill_mainloop()
{
quit = true;
}
static bool restart_in_atlas = false;
// called by game code to indicate main() should restart in Atlas mode
// instead of terminating
void restart_mainloop_in_atlas()
{
quit = true;
restart_in_atlas = true;
}
static bool restart = false;
// trigger an orderly shutdown and restart the game.
void restart_engine()
{
quit = true;
restart = true;
}
extern CmdLineArgs g_args;
// moved into a helper function to ensure args is destroyed before
// exit(), which may result in a memory leak.
static void RunGameOrAtlas(int argc, const char* argv[])
{
CmdLineArgs args(argc, argv);
g_args = args;
// We need to initialise libxml2 in the main thread before
// any thread uses it. So initialise it here before we
// might run Atlas.
CXeromyces::Startup();
// run Atlas (if requested via args)
bool ran_atlas = ATLAS_RunIfOnCmdLine(args, false);
// Atlas handles the whole init/shutdown/etc sequence by itself;
// when we get here, it has exited and we're done.
if(ran_atlas)
return;
// run non-visual simulation replay if requested
if (args.Has("replay"))
{
std::string replayFile = args.Get("replay");
if (!FileExists(OsPath(replayFile)))
{
debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
return;
}
Paths paths(args);
g_VFS = CreateVfs(20 * MiB);
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE);
MountMods(paths, GetMods(args, INIT_MODS));
{
CReplayPlayer replay;
replay.Load(replayFile);
replay.Replay(args.Has("serializationtest"), args.Has("ooslog"));
}
g_VFS.reset();
CXeromyces::Terminate();
return;
}
// If visual replay file does not exist, quit before starting the renderer
if (args.Has("replay-visual"))
{
std::string replayFile = args.Get("replay-visual");
if (!FileExists(OsPath(replayFile)))
{
debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
return;
}
}
// run in archive-building mode if requested
if (args.Has("archivebuild"))
{
Paths paths(args);
OsPath mod(args.Get("archivebuild"));
OsPath zip;
if (args.Has("archivebuild-output"))
zip = args.Get("archivebuild-output");
else
zip = mod.Filename().ChangeExtension(L".zip");
CArchiveBuilder builder(mod, paths.Cache());
// Add mods provided on the command line
// NOTE: We do not handle mods in the user mod path here
std::vector mods = args.GetMultiple("mod");
for (size_t i = 0; i < mods.size(); ++i)
builder.AddBaseMod(paths.RData()/"mods"/mods[i]);
builder.Build(zip, args.Has("archivebuild-compress"));
CXeromyces::Terminate();
return;
}
const double res = timer_Resolution();
g_frequencyFilter = CreateFrequencyFilter(res, 30.0);
// run the game
int flags = INIT_MODS;
do
{
restart = false;
quit = false;
if (!Init(args, flags))
{
flags &= ~INIT_MODS;
Shutdown(SHUTDOWN_FROM_CONFIG);
continue;
}
InitGraphics(args, 0);
MainControllerInit();
while (!quit)
Frame();
Shutdown(0);
MainControllerShutdown();
flags &= ~INIT_MODS;
} while (restart);
if (restart_in_atlas)
{
ATLAS_RunIfOnCmdLine(args, true);
return;
}
// Shut down libxml2 (done here to match the Startup call)
CXeromyces::Terminate();
}
#if OS_ANDROID
// In Android we compile the engine as a shared library, not an executable,
// so rename main() to a different symbol that the wrapper library can load
#undef main
#define main pyrogenesis_main
extern "C" __attribute__((visibility ("default"))) int main(int argc, char* argv[]);
#endif
extern "C" int main(int argc, char* argv[])
{
#if OS_UNIX
// Don't allow people to run the game with root permissions,
// because bad things can happen, check before we do anything
if (geteuid() == 0)
{
std::cerr << "********************************************************\n"
<< "WARNING: Attempted to run the game with root permission!\n"
<< "This is not allowed because it can alter home directory \n"
<< "permissions and opens your system to vulnerabilities. \n"
<< "(You received this message because you were either \n"
<<" logged in as root or used e.g. the 'sudo' command.) \n"
<< "********************************************************\n\n";
return EXIT_FAILURE;
}
#endif // OS_UNIX
EarlyInit(); // must come at beginning of main
RunGameOrAtlas(argc, const_cast(argv));
// Shut down profiler initialised by EarlyInit
g_Profiler2.Shutdown();
return EXIT_SUCCESS;
}