As we all know, the only moment when we can use left mouse button for spawn, is actually at our first spawn in a server.
Even if this is not a really good idea (to fix it by script), I'm showing you the easiest way to do it.
(until developers fix it in client)
The main idea is that we check if a player that is not yet spawned, binds left click. If it does press the button, we force it's spawn.
BIND_LEFT_MOUSE <- null;
function onScriptLoad()
{
BIND_LEFT_MOUSE = BindKey( true, 0x01, 0, 0 );
}
function onKeyDown( player, key )
{
if( key == BIND_LEFT_MOUSE && !player.IsSpawned ) player.Spawn( );
}
BUT (yes there's a BUT!), I'd like to raise a few questions. The player shoots with the mouse. Therefore, wouldn't that generate a massive amount of event calls on a full server? And, would the old shoot/spawn event still work if you bind this key? I'm just curious.
Quote from: S.L.C on Mar 28, 2015, 09:41 PMThe player shoots with the mouse. Therefore, wouldn't that generate a massive amount of event calls on a full server?
Moving upper the if( !player.IsSpawned ) should reduce the lag that could be caused.
if( !player.IsSpawned && key == BIND_LEFT_MOUSE ) Message( "binded" ); player.Spawn( );
Quote from: S.L.C on Mar 28, 2015, 09:41 PMAnd, would the old shoot/spawn event still work if you bind this key? I'm just curious.
Yep, it still works.
player.Spawn( ) is calling the onPlayerSpawn event.
onPlayerActionChange is called when a player fires.
Btw, good questions. :)
Quote from: sseebbyy on Mar 28, 2015, 10:11 PMMoving upper the if( !player.IsSpawned ) should reduce the lag that could be caused.
Actually not really. Let me explain what happens in the scripting plugin whenever there's keypress event:
You know that at the root of all things in your script there's one huge global table that holds the global elements. I.e. the root table. And the more you add to that table the more it takes to search for things.
Well, that table can get quite busy. Especially since all the script-writers use the
<- instead of
= and make all things global. Which is why I always insist on people to stop making all things global. To have an idea of how many things are in that global table use the following:
print( getroottable().len() );
Anyway, each time there's a server event, the Squirrel plugin looks for an element in that table with the name of the event. The following code illustrates that (should output "function"):
function onKeyDown(player, key)
{
//...
}
print( typeof getroottable().rawget("onKeyDown") );
And if the found element is of type function. Remember that Squirrel is a dynamically typed language and every variable or table element can be anything. Anyway, if the found element is a function then it proceeds to calling it.
Calling that function involves a few more operations like pushing the function arguments onto a stack and then actually calling the function which pops the arguments from the stack. Not to mention the things you have to verify in the function like IsSpawned() which is another function call and the rest of the conditions etc. Which result in a ton of instructions to do on each event.
Don't get me wrong, your CPU isn't stupid and can take all of that. But if you start adding other events and other code then it starts to feel heavy. Not to mention that there's no filtering like in the plugin that I'm working on. So, you get a bunch of event calls that you'll ignore anyway.
But all that is just tl;dr and it's worthless. When I could simply have said: This shouldn't be done with scripts. Which comes to my proposal. Would it be ok if I made this as a C/C++ plugin to be as fast as possible? And then post it here. Would take just a few minutes to make.
Quote from: S.L.CBut all that is just tl;dr and it's worthless
Not really, I understood much part of what you said.
( didn't know about the roottable, and etc )
Unfortunately, I have no knowledge about C/C++ , so there is no way for me to write a plugin.
(if I knew how to, scripts like CMS would be as plugins, haha)
Quote from: S.L.CEspecially since all the script-writers use the <- instead of = and make all things global. Which is why I always insist on people to stop making all things global.
I updated the first script (should be better now), and I'm pretty sure I will practice what you said in my scripts.
Well, this is all I can do via scripts.
If you are going to write the plugin for it, would be great, and maybe developers will use it too, in the next update.
He meant not to use <-
Quote from: rObInX on Mar 28, 2015, 11:07 PMHe meant not to use <-
It's ok to use that with keybinds because you can't create more keybinds for the same key. And therefore making it global would allow others to use that keybind.
I still have doubts about the difference between these two ways I have used:
- making it global by directly giving the value
- making it global by giving null value, then giving the wanted value onScriptLoad
I have seen that in stormeus' released script (the one used in last pb):
BIND_HYDRAULICS_LEFT <- null;
function onScriptLoad( )
{
BIND_HYDRAULICS_LEFT = BindKey( true, 0x64, 0, 0 );
}
I concluded that doing it like this, will not help in making more busy the root table, because I think the null values won't be counted until it is used.
But I can be wrong, so what's the difference between these 2 ways ?
Second time not working for me. ???
Quote from: sseebbyy on Mar 28, 2015, 11:26 PMbecause I think the null values won't be counted until it is used.
Actually NO! Let me give an example and imagine that my_table is the root/global table (
because it's the same):
local my_table = {}; // Empty root table
print(my_table.len()) // Should be 0
my_table.my_element <- null;
print(my_table.len()) // Should be 1
It doesn't matter if an element is null or not. Believe it or not, but null, is also a type of data. Once you create an element in that table then it stays there until you explicitly remove it. So I could do:
my_global_variable <- "wazzaaaa";
print( getroottable().my_global_variable ) // You should see: wazzaaaa
print( getroottable()["my_global_variable"] ) // You should see: wazzaaaa
print( getroottable().rawget("my_global_variable") ) // You should see: wazzaaaa
getroottable().rawdelete("my_global_variable");
print( getroottable().my_global_variable ) // That index doesn't exist anymore
print( getroottable()["my_global_variable"] ) // That index doesn't exist anymore
print( getroottable().rawget("my_global_variable") ) // That index doesn't exist anymore
The reason he creates them as null and then initializes them in OnLoad() is because the plugin API isn't initialized at that time. And if you call any of it's functions then your server will crash. At least that's how it used to be. Now the scripts are executed when the server calls OnInit() and the plugin API is already initialized. Probably just a habit from before.
But the actual reason I think is because it allows him to know what global variables are available. Because using <- silently in a function can stay unnoticed until you make another one with the same name. And when that happens you overwrite the first one and things could go bad. Which is another good example of why <- should be avoided in the root/global table.
Things are now much clear. Mulțumesc. 8)
Good idea, ill add it to my server.
Someone joined yesterday and quit everytime they died to rejoin, knew they had the bug and didn't speak English so I couldn't help.
:edit: O0 O0
Quote from: Gulk on Mar 29, 2015, 07:22 AMGood idea, ill add it to my server.
Someone joined yesterday and quit everytime they died to rejoin, knew they had the bug and didn't speak English so I couldn't help.
:edit: O0 O0
hhhhh
sorry it was me i was testing some key shortcut to /q when i press that button (to death evade ofc :<).
i wanted to join again but no battery left (in laptop) for that.
p.s i do know how to speak english a lil bit.
Quote from: S.L.C on Mar 28, 2015, 09:41 PMThe player shoots with the mouse. Therefore, wouldn't that generate a massive amount of event calls on a full server?
You mentioned the shoot key in specific. Does the event get called when a player presses any key, irrespective if it has a binding or not?
Think of 30 hotheads smashing their keyboard and mouse in an intense battle. Does that call onKeyDown(player, null) and onKeyUp(player, null) on every key press?
Quote from: aXXo on Mar 29, 2015, 02:37 PMThink of 30 hotheads smashing their keyboard and mouse in an intense battle. Does that call onKeyDown(player, null) and onKeyUp(player, null) on every key press?
Yes, as long as a key is binded you'll receive a call through that function. Even if you don't need that event sometimes.
I still don't understand why you guys have to make global variables for keybinds
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 );
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == 0 ) player.Spawn();
}
or if you really want variables you can use enumerators instead
enum Keys
{
LeftMouseBtn
}
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 ); //Add into enum keys in the same order as this!!!
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == Keys.LeftMouseBtn ) player.Spawn();
}
Quote from: NE.CrystalBlue on Mar 29, 2015, 04:46 PMI still don't understand why you guys have to make global variables for keybinds
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 );
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == 0 ) player.Spawn();
}
What makes you so sure that the keybind you previously created will have an ID of 0? What makes you so sure that someone else didn't use BindKey() previously, in which case you might end up spawning the player using some other random key. Hard coded values in this context.... might be the worst idea ever!
Quote from: NE.CrystalBlue on Mar 29, 2015, 04:46 PMor if you really want variables you can use enumerators instead
enum Keys
{
LeftMouseBtn
}
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 ); //Add into enum keys in the same order as this!!!
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == Keys.LeftMouseBtn ) player.Spawn();
}
Constants and Enumerations are processed at compile-time. That means when the script is read from your storage unit and transformed into byte-code. And you can only call BindKey() after the compilation, when the actual byte-code is executed. How are you going to trick this?
Yes I could trick this using dofile() to have a second file with delayed compilation, and getconsttable() to manipulate the constants table from the parent file before dofile() is executed. Which would cause the constants created in the parent file to already be available before the second file is even compiled. But I'd still have to rely on some unreliable methods to achieve that. And what's the purpose then.
Quote from: S.L.C on Mar 29, 2015, 04:57 PMQuote from: NE.CrystalBlue on Mar 29, 2015, 04:46 PMor if you really want variables you can use enumerators instead
enum Keys
{
LeftMouseBtn
}
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 ); //Add into enum keys in the same order as this!!!
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == Keys.LeftMouseBtn ) player.Spawn();
}
Constants and Enumerations are processed at compile-time. That means when the script is read from your storage unit and transformed into byte-code. And you can only call BindKey() after the compilation, when the actual byte-code is executed. How are you going to trick this?
Yes I could trick this using dofile() to have a second file with delayed compilation, and getconsttable() to manipulate the constants table from the parent file before dofile() is executed. Which would cause the constants created in the parent file to already be available before the second file is even compiled. But I'd still have to rely on some unreliable methods to achieve that. And what's the purpose then.
No, I mean that make the enums in the very same order which you binded the keys.
For example, if you first bind key x then bind key y then z use
enum Keys
{
X,
Y,
Z
}
I know that they are processed precompilation but thats exactly the very reason I preferred them over variables.
Quote from: NE.CrystalBlue on Mar 29, 2015, 06:21 PMNo, I mean that make the enums in the very same order which you binded the keys.
Exactly one of two things happen right now and I don't know which. Either, I don't get what you're saying or you don't get what you're saying. So, I'd like some input from a third (
knowledgeable) person to see which.
Quote from: NE.CrystalBlue on Mar 29, 2015, 04:46 PMenum Keys
{
LeftMouseBtn
}
function onScriptLoad()
{
BindKey( true, 0x01, 0, 0 ); //Add into enum keys in the same order as this!!!
}
function onKeyDown( player, key )
{
if( !player.IsSpawned && key == Keys.LeftMouseBtn ) player.Spawn();
}
I'm not one to debate semantics but isn't this the exact same thing as creating a global table called "Keys" with a "LeftMouseBtn" member?
Global variables have virtually zero impact on the performance of Squirrel. The important thing is code readability. If you have one file that creates a keybind on the left mouse button and another file holding the onKeyDown event, which is more readable?
if (key == 0)
or
if (key == KEYBIND_LMOUSE)
Also, it's generally a
very bad idea™ to
not name your keybinds; if another script or another plugin creates a keybind, there is
no guarantee your keybind ID is correct.
Quote from: stormeus on Mar 29, 2015, 07:56 PMI'm not one to debate semantics but isn't this the exact same thing as creating a global table called "Keys" with a "LeftMouseBtn" member?
According to Squirrel documentation (
and I might be wrong about this). Enums, being evaluated at compile time, would have their value in-place when used. As an example, the following code:
enum MyE
{
elemX = 2
}
print(MyE.elemX);
Would result as:
print(2);
Similar to the macros in C/C++ etc. But I might be wrong. The documentation was very vague about this.
When using tables, that same code would result in more byte code near the print function. Something like:
MyE <-
{
elemX = 2
}
// Push table name on stack: MyE
// Perform a get operation on the root table.
// Which pushes the MyE table on stack now.
// Push element name on stack: elemX
// Perform a get operation on the table from the stack.
// Which pushes the value 2 on stack now.
// Now call the print function with the arguments on stack. I.e. 2
print(MyE.elemX);
Quote from: stormeus on Mar 29, 2015, 07:56 PMAlso, it's generally a very bad idea™ to not name your keybinds; if another script or another plugin creates a keybind, there is no guarantee your keybind ID is correct.
That's the exact same thing I was trying to say.
Anyway, this is the trick I was talking about earlier with constants and delayed scripts.
main.nut
function onServerStart()
{
// Create a constant named 'KEYBIND_LMOUSE_PRESS' with the id of the key-bind
getconsttable().rawset( "KEYBIND_LMOUSE_PRESS", BindKey( true, 0x01, 0, 0 ) );
// That constant is available before even compiling this file
dofile("delayed.nut");
}
delayed.nut
function onKeyDown( player, key )
{
// 'KEYBIND_LMOUSE_PRESS' will be replaced by
// the literal value inside that constant
if( !player.IsSpawned && key == KEYBIND_LMOUSE_PRESS ) player.Spawn();
}
Quote from: S.L.C on Mar 29, 2015, 08:29 PMAnyway, this is the trick I was talking about earlier with constants and delayed scripts.
main.nut
function onServerStart()
{
// Create a constant named 'KEYBIND_LMOUSE_PRESS' with the id of the key-bind
getconsttable().rawset( "KEYBIND_LMOUSE_PRESS", BindKey( true, 0x01, 0, 0 ) );
// That constant is available before even compiling this file
dofile("delayed.nut");
}
delayed.nut
function onKeyDown( player, key )
{
// 'KEYBIND_LMOUSE_PRESS' will be replaced by
// the literal value inside that constant
if( !player.IsSpawned && key == KEYBIND_LMOUSE_PRESS ) player.Spawn();
}
I wonder if the same will work with compilescript();
function onServerStart()
{
// Create a constant named 'KEYBIND_LMOUSE_PRESS' with the id of the key-bind
getconsttable().rawset( "KEYBIND_LMOUSE_PRESS", BindKey( true, 0x01, 0, 0 ) );
// That constant is available before even compiling this file
local keydownfunc = compilescript(@"function onKeyDown(player, key)
{
if( !player.IsSpawned && key == KEYBIND_LMOUSE_PRESS ) player.Spawn();
}");
keydownfunc();
}
Quote from: NE.CrystalBlue on Mar 30, 2015, 04:31 AMI wonder if the same will work with compilescript();
It's exactly the same thing. The only difference is that compilestring() won't look very pretty when you add more keybinds and more code.
Quote from: S.L.C on Mar 29, 2015, 07:30 PMQuote from: NE.CrystalBlue on Mar 29, 2015, 06:21 PMNo, I mean that make the enums in the very same order which you binded the keys.
Exactly one of two things happen right now and I don't know which. Either, I don't get what you're saying or you don't get what you're saying. So, I'd like some input from a third (knowledgeable) person to see which.
But I still want to explain it to you. Key bind ids are created in the very order the binds were created. Same thing with enumerators. So in this enumerator
enum Keys
{
LeftMouseBtn,
RightMouseBtn,
F2
}
so LeftMouseBtn has a value of 0, RightMouseBtn has a value of 1 and F2 has a value of 2.
Now if you keybind
BindKey(...);//This will make a key and if you use key == Keys.LeftMouseBtn in onKeyDown then this is the key assigned to that since this is id 0
BindKey(...);//This will make a key and if you use key == Keys.RightMouseBtn in onKeyDown then this is the key assigned to that since this is id 1
BindKey(...);//This will make a key and if you use key == Keys.F2 in onKeyDown then this is the key assigned to that since this is id 2
Simple and readable but will require the user of the script to do this with his keys also.
Quote from: NE.CrystalBlue on Mar 30, 2015, 04:41 AMBut I still want to explain it to you. Key bind ids are created in the very order the binds were created. Same thing with enumerators. So in this
The way you want to achieve that is still dangerous because the returned IDs may vary. You could use the same delay trick for enumerations as well:
function onServerStart()
{
// Create an enum named 'KeyBinds' with the ids of the key-binds
getconsttable().rawset( "KeyBinds", {
LMOUSE_PRESS = BindKey( true, 0x01, 0, 0 ),
LMOUSE_RELEASE = BindKey( false, 0x01, 0, 0 ),
RMOUSE_PRESS = BindKey( true, 0x02, 0, 0 ),
RMOUSE_RELEASE = BindKey( false, 0x02, 0, 0 )
});
// That enum is available before even compiling this file
local keydownfunc = compilestring(@"
function onKeyDown( player, key )
{
switch (key)
{
case KeyBinds.LMOUSE_PRESS: break; // Left mouse press
case KeyBinds.LMOUSE_RELEASE: break; // Left mouse release
case KeyBinds.RMOUSE_PRESS: break; // Right mouse press
case KeyBinds.RMOUSE_RELEASE: break; // Right mouse release
}
}");
keydownfunc();
}
And even better now you can use a switch case, which is much faster for many equality conditions.
Also, a suggestion to @sseebbyy. Verify the keybind ID first to avoid a function call when it's not even the key you need:
function onKeyDown( player, key )
{
if( key == BIND_LEFT_MOUSE && !player.IsSpawned ) player.Spawn( );
}
Quote from: S.L.C on Mar 30, 2015, 04:54 AMAlso, a suggestion to @sseebbyy. Verify the keybind ID first to avoid a function call when it's not even the key you need:
function onKeyDown( player, key )
{
if( key == BIND_LEFT_MOUSE && !player.IsSpawned ) player.Spawn( );
}
I disagree. If I will do your way,, the event will be called everytime a player shoots. (and here comes your e.g. about 100 players firing at the same time; that's more possible, when there is a war)
Using my way, the event will be called whenever an unspawned player presses a binded key.
(there is a small chance to be 100 players in the spawnscreen, and an even more small chance that they press other key than the binded LMB)
The event get's called no mater if the player is spawned or not. My concern was that you always check IsSpawned (which is actually a function, internaly) even if the key is not the one you expect. Which is why, comparing the key first, eliminates most of the useless calls. Comparing two integers is faster then calling a function.
Ye, now it seems better.
First post was updated. :)
A much needed solution is to add several bool attributes to the current classes similar to object.TrackingBumps
Example:
player.TrackingKeys = true would enable monitoring OnPlayerKeyDown for that player. Any other player pressing a key would have no impact on the server performance. I mentioned the same in my post here (http://forum.vc-mp.org/?topic=292.msg1684#msg1684), but I hope this is feasible in an update to the official Squirrel plugin.
Quote from: aXXo on Mar 30, 2015, 11:29 PMA much needed solution is to add several bool attributes to the current classes similar to object.TrackingBumps
Example:
player.TrackingKeys = true would enable monitoring OnPlayerKeyDown for that player. Any other player pressing a key would have no impact on the server performance. I mentioned the same in my post here (http://forum.vc-mp.org/?topic=292.msg1684#msg1684), but I hope this is feasible in an update to the official Squirrel plugin.
That's the exact same thing I've been trying to feature on that module. The ability to avoid unnecessary event calls. I've almost done that. Now I just need to deal with a few flaws in my design and I'll start completing the module.
A small (untested) C++ version of this can be found bellow:
#include <stdio.h>
#include <string.h>
#include <vcmp.h>
#if defined(_MSC_VER)
#define SQMOD_API_EXPORT extern "C" __declspec(dllexport)
#elif defined(__GNUC__)
#define SQMOD_API_EXPORT extern "C"
#endif
PluginFuncs * gFuncs;
int SPAWN_KEY_ID = -1;
void OnKeyBindDown(int nPlayerId, int nBindId)
{
if (nBindId == SPAWN_KEY_ID && !gFuncs->IsPlayerSpawned(nPlayerId)) {
gFuncs->ForcePlayerSpawn(nPlayerId);
}
}
int OnInitServer()
{
SPAWN_KEY_ID = gFuncs->RegisterKeyBind(gFuncs->GetKeyBindUnusedSlot(), 1, 0x01, 0, 0);
if (SPAWN_KEY_ID < 0) {
printf( "Left Click Spawn for VC:MP failed to register a key-bind" );
return 0;
}
printf( "Left Click Spawn for VC:MP successfully loaded" );
return 1;
}
SQMOD_API_EXPORT unsigned int VcmpPluginInit( PluginFuncs * functions, \
PluginCallbacks * callbacks, \
PluginInfo * info )
{
info->uPluginVer = 0x1000; // 1.0.00
strcpy( info->szName, "Left Click Spawn for VC:MP" );
gFuncs = functions;
callbacks->OnInitServer = OnInitServer;
callbacks->OnKeyBindDown = OnKeyBindDown;
return 1;
}
Code::Blocks project can be found here (http://s000.tinyupload.com/?file_id=26840974858146356048). If this works than let me know so that I can compile that on all platforms.
Knowing that isspawned is actually a function, I actually feel like using variables for checking if the player is spawned.
Quote from: NE.CrystalBlue on Mar 31, 2015, 07:13 AMKnowing that isspawned is actually a function, I actually feel like using variables for checking if the player is spawned.
Perhaps you quoted the wrong post because I don't know what you mean.
Quote from: S.L.C on Mar 31, 2015, 07:22 AMQuote from: NE.CrystalBlue on Mar 31, 2015, 07:13 AMKnowing that isspawned is actually a function, I actually feel like using variables for checking if the player is spawned.
Perhaps you quoted the wrong post because I don't know what you mean.
Sorry I forgot to remove the quote. Thats how I reply (quote and remove quote, its faster). I edited my post now.
Quote from: S.L.CIf this works than let me know so that I can compile that on all platforms.
Nope, it doesn't work. I see the ""Left Click Spawn for VC:MP successfully loaded"" message in console, but when I press LMB, there is nothing happening.
Btw, didn't you say that calling a function is slower than comparing integers ?
Quote from: S.L.Cvoid OnKeyBindDown(int nPlayerId, int nBindId)
{
if (gFuncs->IsPlayerSpawned(nPlayerId) && nBindId == SPAWN_KEY_ID) {
gFuncs->ForcePlayerSpawn(nPlayerId);
}
}
I have some issues with my monitor right now but I'll verify that plugin as soon as I can get back on my PC.
EDIT: I think I forgot to negate the result from IsPlayerSpawned(). I'll be back with a fix.
EDIT: Updated the post with the code. Try that and let me know.
Quote from: S.L.C on Mar 31, 2015, 03:55 PMEDIT: Updated the post with the code. Try that and let me know.
It still doesn't work. Nothing happens, just like before.
Testing it by yourself will make you realize faster what's the problem. ;)
Quote from: sseebbyy on Apr 01, 2015, 11:57 AMTesting it by yourself will make you realize faster what's the problem. ;)
I'm having some issues with my monitor and I'm not at my PC right now. I will actually test this when I get a new one or at least get a provisory monitor.
Since this was fixed by developers, I will just lock this.