- The new code works with my router while libupnp didn't. :)
- There are some unexplainable crashes in libupnp: #1640
- Using miniupnpc seems to be less complex than libupnp.
- Apparently, miniupnpc also works on Windows, so we may be able to use
it for all platforms.
Disadvantage: UPnP queries aren't asynchronous anymore, but they seem to
be pretty fast (< 1 s).
It could happen that the objects were cleared (after evaluation screen / on section change) but a particle list would remain that would still point to an object (and would then access it).
I am not exactly sure how that could happen, because objects should clear their particle lists on removal (and thus shouldn't really need a ClearPointers).
There is a tiny chance that this points to another bug somewhere in the object removal - that's just a random guess though.
Anyway, this should fix the infamous Knüppeln crash.
Declaring a local variable inside any function works the same way as
declaring it in top-level scope, which gets a bit weird if done inside a
function inside a constant proplist, but is at least consistent.
Thanks to Flinti for reporting this.
On High-DPI displays, the mouse cursor is very tiny. We'll probably want
some high-resolution cursor graphics at some point, but the current ones
scale good enough.
All the other effect callbacks silently ignored missing functions, either
by using the Call variant that doesn't complain about it, or checking for
the missing function themselves.
Effects with prototypes were supposed to inherit their names from the
prototype, but the effect prototypes are also supposed to get their names
from ParentKeyName, which is not inherited. Maybe that'll change, but for
now this matches how old effects work.
Apparently this is recommended so that a std::vector actually uses the
move constructor. In any case, the second parameter wasn't used, so this is
a nice simplification as well.
Employ variadic template arguments and more rvalue references for this.
Sadly, StdParameterAdapt itself is even more complicated, since it has to
store the parameters instead of just forwarding them, so the limit is still
two parameters. But that's twice as much as before in many cases.
This commit contains a fairly substantial rewrite of the C4Script code
generator. Instead of generating bytecode while parsing the script,
we're now parsing the script into a syntax tree, and have any further
processing happen on that instead of the raw source.
At this time, the code generator emits the same bytecode as the old
parser; there are several optimization opportunities that arise from the
new possibility to emit code out of order from its specification by the
author.
Compared to the old compiler, this one is still rather deficient when
dealing with incorrect code; it's also not emitting several warnings
that used to be diagnosed.
Thanks to ck, who noticed. Apparently it got lost when reverting some changes back and forth for testing, because I had one revision where I didn't need it anymore but I reverted most of that.
Some particles were visibly missing. The reason was apparently that the VAO was initialized with an IBO that was yet too small (because not all particles were created yet).
To be honest, I don't know why the IBO needs to be rebound as the identifier itself does not change(?) but maybe OpenGL optimizes some stuff and needs the size fixed?
Steps to reproduce: In OneMillionParticles.ocs, make Test3 be the first test (otherwise the IBO would have been large enough). You would now not see all of the fireworks.
The issue was introduced in e13cda4bd0
Otherwise the IBO is created from scratch for every particle that is e.g. created in a for loop. This is extremely slow and unnecessary.
The symptoms was that OneMillionParticles appeared to 'freeze' on the non-batch creation test. (Hint: it didn't freeze, it just took forever).
The C++ standard says that it doesn't matter whether you forward declare
something as struct or class and then define it using the other keyword.
Unfortunately MSVC adds those keywords to its name mangling scheme,
which breaks things.
Unfortunately, there doesn't seem to be an easy way to apply
multisampling changes in SDL. As a workaround, a message requesting the
player to restart the game is shown.
This unifies the lookup for local variables and functions. Its slightly
awkward that global functions can overload if and Par now, but local
variables already could.
Since GRBroadcast calls all goal and rule objects and the scenario,
there is a high likelyhood that not all of them implement whatever
arbitrary callback is called. Instead of throwing script errors about
calling undefined functions, just swallow that error and continue.
This should eventually replace the Prototype property, so that proplists
can be used as a key-value-storage without any hidden gotchas.
The Prototype is such a magical property that any code dealing with all
properties has to special-case it anyway, and isn't even returned by
GetProperties().
This even enables some simplification in the CompileFuncs, since the global
effects were already put in the same section as the other ScriptEngine
parts. The callback for updates due to relinks also fits nicely.
The reset in C4Game::Default was redundant with C4Game::Clear already.
This accessor does not need to be virtual itself, because we don't want
people to override it; it calls the nonconst accessor anyway, which is
virtual.
Several issues:
- The RNG wasn't copied with the value provider (which is always
copied), resulting in unseeded RNGs.
- Separate PCG streams with the same seed start with identical values.
As particles often draw only a single value, we just always advance
the RNG a bit to make the streams diverge.
- The C++ specification requires a <= b for its distributions, but that
wasn't guaranteed by the particle startValue/endValue.
The window would always use its very own rectangle for clipping - bad luck if it was larger than the parent.
Now children can only additionally restrict the parent rectangle (which defaults to the whole screen at root).
Discovered with valgrind. Previously, some memory that later gets freed using
free(...) (in StdBuf::Clear) was allocated with new char[...] rather than
malloc, in which case delete[] should be used to free them.
This will make lighting look weird again for meshes flipped via FlipDir.
Avoid doing that until we implement FlipDir for meshes via an internal
MeshTransform.
a) make sure the context is deselected on destruction, so that
CStdGL::Clear() destructor doesn't try to deselect a non-existing context.
b) Calling Clear() in CStdGLCtx::~CStdGLCtx() does only call
CStdGLCtx::Clear(), even though Clear() is virtual. The reason is that by
the time the CStdGLCtx destructor is executed, the CStdGLCtxQt part of the
object has already been destructed. Therefore, make CStdGLCtx::Clear() safe
to be run without the context ever having been initialized, and explicitly
call CStdGLCtxQt::Clear() in CStdGLCtxQt::~CStdGLCtxQt(). This is certainly
not the most elegant way to handle this, but it should do the job for now.
Instead of making the user count lines from the shader log, we'll now
emit #line directives and a file number->name mapping to make it easier
to figure out which file and line the error message comes from.
Since LTCG is enabled now, we don't have to define every function inside
the headers for ~xXx super speed xXx~, which means we can strip the
headers down to their bare minimum and reduce interdependencies and
therefore recompilation times by a lot.
Mesh rotation needs to happen as part of the MeshTransform so lighting
is applied correctly. DrawTransform applies after lighting which made
rotated meshes look weird.
The SDL port used to define a separate set of key codes differing from
the usual ones. The SDL codes are now translated to make the Qt input
easy.
This also fixes a broken keycode for the '0' key.
The QOpenGLWidget is set up to render into a custom framebuffer, and by
resetting to render into the default framebuffer, we don't draw anything in
the buffer that is actually going to be displayed by the widget.
The plane limits in C4ObjectList::Draw were not properly checked,
leading to objects in front of the landscape being drawn twice (once
before the landscape was rendered, and once after).
The landscape renderer used to store texture data into the default 1D
texture, which works but is a bad idea for several reasons (and would
have broken if we had a reason to use another 1D texture anywhere else).
The available gamepads are distributed automatically among players.
This also implements controller hot-plugging: It is possible to start a
game without a controller and plug it in later, and to reconnect a
controller after plugging it out.
We want one gamepad key mapping to work with multiple gamepads, so
including the id there doesn't make sense.
Additionally, the gamepad id may change during the game (controller
hot-plugging).
The icons currently only show Xbox 360 controller labeling. The icon set
also includes icons for PlayStation controllers, so we could extend this
in the future.
This also removes the controller id from the control definitions,
instead defaulting to 0. This doesn't change anything for now as we only
had definitions for controller 0 anyways.
It is now possible to control all GUI menus using the left stick or the
dpad, along with the A and B buttons on the controller.
This also doubles the button emulation dead zone to make navigating the
menus with the stick easier.
To keep compatibility with scripts which expect only binary buttons,
this adds a third parameter to the function which enables the new
functionality.
Bonus /script to test the controller stick x axis:
Schedule(GetCursor(), "Message(\"%d / %d\", GetPlayerControlState(0, CON_Left, true), GetPlayerControlState(0, CON_Right, true))", 10, 100000000)
Note that the values will be inconsistent if multiple analog sticks are
bound to the same control, as values from one stick will overwrite those
from the other one. This can happen even if you move only one stick.
With the SDL_GameController interface, buttons and axes have actual
names we can refer to. This also allows for advanced mappings using both
sticks (this probably needs script changes) as well as the triggers.
Previously, global particles would be drawn even in front of Plane 1000+ (GUI) objects. Additionally, it was impossible to specify particles that were supposed to always be in front of all other particles (e.g. fog, clouds, emulated FoW, ...).
Now, you can attach particles to an object on plane 901+ and have them be in front of everything else.
The FoW renderer would try drawing to texture coordinates one pixel
too high (i.e. if the texture was 256 pixels high, it would draw to
pixels with 1 <= y <= 256 instead of 0..255).
The lpClass parameter isn't used, and it is valid to pass NULL for it.
It is, however, not valid to pass a string literal, because the
conversion to nonconst char* has been deprecated in C++ for a long time
and doesn't exist anymore in C++11.
Quoth MSDN: "The Unicode version of this function, CreateProcessW, can
modify the contents of this string. Therefore, this parameter cannot be
a pointer to read-only memory (such as a const variable or a literal
string). If this parameter is a constant string, the function may cause
an access violation."
It can, however, be NULL, which does the right thing automatically.
It seems like an odd decision to add a layer of indirection to this,
especially since it's just a pointer to int instead of a pointer to a
larger structure.
The red color channel calculation could overflow into the sign bit,
which is undefined behavior. At least one compiler takes advantage of
this and assumes it cannot happen, resulting in incorrect results.
BltAlphaAdd looks similar, but does in fact not have this bug because it
shifts the color channel far enough that multiplication can't overflow.
Otherwise, you could open a menu on mouse-down, which would then block the mouse-up event. The control system (not the script but the engine!) would then never know that the button was released and issue a key event with repeated=1 when you pressed the button the next time.
This could lead to issues.
If the gamepad code initialized the SDL video subsystem, GTK+ crashed in
libX11. Or something along those lines.
Making the optional subsystems using SDL for gamepads and audio use
SDL_InitSubSystem and only the SDL Application port do the full SDL_Init
is the proper way to do things in any case.
It used a mix of tabs and spaces for indentation, and tabs to separate the
columns, which doesn't work with variable tab sizes and variable column
widths. Use tabs for indentation and spaces for column separation.
This revealed that the code for setting Par.i was wrong before, but didn't
matter because the jump target for all CONDN was set afterwards. But the
jumptarget for COND was set directly, and must be adjusted to account for
the bytecode that gets optimized away.
It pointed always to Pars + Func->GetParCount(). The only users were
VARN_CONTEXT, which can be replaced with PARN_CONTEXT that way, and
FOREACH_NEXT, where the parser can do the math and simply give a number
relative to the current stack position, like regular variable usages do.
This can happen when an engine function is made to call a script function
when the stack is almost exhausted. Since there was nothing catching that
exception, it'd mean a program abort. Move the bulk of the exception
handler to the outer Exec function, and only keep the saving of the exact
code position in the inner one.
While at it, simplify the pushing of the parameters to simply push the
right amount of parameters instead of pushing ten and then popping the
excess. Also move the creation of dummy parameters for DirectExec into
DirectExec().
In earlier C4Script versions, there were multiple variants of expressions,
all followed by the same operator options, but nowadays everything is more
uniform.
The GTK and OS X platforms already ignored the requested bit depth and
always used 32 bit. Windows and SDL would set a 16 bit color depth for
the screen, but still did all of the rendering short of the final
present in 32 bit.
In windowed mode, we shouldn't stop rendering just because we have lost
focus. However, we don't need to render at full framerate; throttling to
5 fps should be sufficient. Note though that we still have to calculate
game ticks at full speed so network games don't slow down when a player
tabs out of the game.
Even though we (might) change the resolution, that doesn't result in
loss of textures because we don't recreate the OpenGL context. Therefore
we also don't have to restore their data. I believe this code is a relic
from the DirectX renderer, which would lose textures when switching away
from a fullscreen window.
When an UI element was only visible to a player (via the Player property), it still allowed ALL players to click on buttons.
I am wondering why noone else noticed that bug before. It's possible that it didn't show when the visibility was set via the menu's Target (instead of the Player property).
Maybe this was the cause of the "clicks sometimes do nothing" bug?
It isn't enough to check the top-level mesh, because attached meshes also
get a transformation from script. At least cotton branches sometimes do
that.
So check the matrix directly before using it and skip rendering.
Unlike GLX, the OpenGL function pointers are context-dependant. Because we
need an extension function to create the context we're going to use, we
first need to create a temporary context. Since this is independent of the
library used to fetch the function pointers, decouple it from glewInit().
It's not clear that the glXCreateNewContext fallback does anyone any good
because it can't create the Core Profile Context we need, but better a
fallback message followed by Shader compilation errors than an obscure
X11 protocoll error. Probably.
This bug manifested itself in mysteriously growing PropLists with nil keys.
Some usages of Clear were simply to clean the newly-allocated table, and
need to continue to do so.
It isn't clear whether that call is necessary since the C4AulScriptEngine
constructor already does this, but it is clear that duplicating the call
all over is a bad idea.
This allows the removal of quite a few return C4Void();.
Also stop pretending that Nillable<void> is the same class as Nillable<T>.
Its only function was as an implementation detail for C4Void, which doesn't
need any implementation details anymore.
This avoids duplication of code in C4AulObjectFunc and C4AulEngineFunc
at the cost of boilerplate code working around the lack of partial
function template spezializations.
Instead of ThisImpl and ExecImpl, we could have multiple C4AulEngineFunc
spezializations deriving from a common template, but that would require
even longer and duplicated boilerplate.
This allows one to use the C4Value constructor instead of
C4ValueConv. To avoid unintended implicit conversions like
const char * to bool, add a private template constructor that
catches everything not in the list of intended constructors.