Jump to content

Muzzleflash

Member
  • Content Count

    1001
  • Joined

  • Last visited

  • Medals

Everything posted by Muzzleflash

  1. Note there are two concurrency issues. One is ensuring multiple scripts work together correctly. That is that they don't intrude on each other. This is currently very simple by running scripts in the unscheduled environment where you can avoid undesired interaction. This is something you will need to extend sqf with features so scripts do not conflict. The second issue, is preventing multiple simultaneous running scripts from crashing Arma. Say you have a two scripts. Script A controls a squad, giving waypoints and such. Script B removes stuff far away from human players. If they can run at the same time, script B might be trying to use some command on a squad member or the group, but then script B deletes the unit at the same time crashing Arma. This can easily be solved by locking the entire game world so only one script may modify it at a time, but then no other script can run anyway making it all pointless. Or you can make more fine grained locking, like locking the squad member so only one script is running any commands related to the unit at the time, but that will be very hard to implement correctly.
  2. That's not even a fraction. You can't use any command with the word 'find' in it because other scripts might be moving the objects at the time. You can't use fog, because another script might be manipulating or reading fog at the same time. You can't use forEach because another script might be modifying the array. Same with formation, or any get* command which may be modified directly by a set* command, or indirectly. Same with resize, road-commands, etc.. So that covers pretty much all commands on that screenshot except pure math ones, like round, + or acos.
  3. Muzzleflash

    Call, Spawn Trouble

    That will work. But if you know how long script1 takes, then I would just wait that amount of time and then start script2 on the server. It avoids issues like having to handle disconnecting/timeout players, otherwise the server will wait forever.
  4. We might have different definitions of "almost all", but if I look at this list https://community.bistudio.com/wiki/Category:Scripting_Commands_Arma_3 there aren't many commands with can run simultaneous without expensive synchronization. You can't use addMagazine because some other script might be reading that. You can't run X setPos Y because someone might be running getPos, or getPosATL, or even "Z distance X", and so on - also you can't run getPos either for the same reason.
  5. Muzzleflash

    Call, Spawn Trouble

    When you use execVM the scripts run in the scheduled environment. This is an environment mean for longer running scripts. Here multiple scripts are active, partially finished, constantly getting paused and then later resumed. You have no guarantees for how long a script takes to complete, or in what order. In fact script2 may start and run to completion before script1 even gets started! Your fastest fix might be to try something like this [{[] call compile preProcessFile "script1.sqf"; [] call compile preProcessFile "script2.sqf"}, "BIS_fnc_spawn", true, false, true] call BIS_fnc_MP;
  6. Look at the bottom of the RPT file at your diagnostic output and the error. In your on-connection handler _uid never get's properly. Try outputting "_this" somehow and see what it is.
  7. Almost all scripting commands interact with shared state: the game world. So very few commands can run at the same time without causing race conditions. And some scripts wait for each other so they wouldn't gain anything. So for most script the blocking overhead need to ensure correctness may even worsen performance. Granted, for a very small range of scripting, doing only pure somewhat intensive calculations, you might benefit from having another thread do work. But doing pure calculations in SQF is not really good use of CPU either. So if your script fits that use case your are probably better off with an extension.
  8. Must admit don't really see why it would not work for your situation. Your solution seems overly complicated with the GUI hack, whereas mine would just have you embed the id. For example: + (format ["<execute expression='[""%1"", 0] call fn_tsk'>Delete task</execute>", _mk]) But if your works fine, then all is well that ends well. Not sure but it looks like if I make an a marker with the text, say "Amber Fall" or something, but makes it in group channel, fn_tsk, may get confused, because the description you search in with 'find' has both _tgtText and _mkText.
  9. Here is an example: My_DeleteTask = { params ["_taskId"]; [_taskId] call BIS_fnc_deleteTask; }; private _id = "TID_DestroyIslandBase"; private _desc = format ["You must destroy the enemy base on this island. Or, if you are lazy, you can choose not to do it by clicking <execute expression='[""%1""] call My_DeleteTask'>here</execute>.", _id]; private _title = "Destroy Base"; [_id, true, [_desc, _title], (getPos player) getPos [1000, random 360], "ASSIGNED", 5, true, true] call BIS_fnc_setTask;
  10. Sure go ahead, do what you want with it :) (yeah my version always draws the vehicles on the map regardless of difficulty settings).
  11. I see now I misunderstand, and you literally meant reveal. I don't have time to test atm. That script is rather intensive if you have a larger group, since the nearunits check runs for each member of the group. Maybe just increase the radius a tiny bit and just do the search once from the player's position? But I am a bit confused about the use case. You only do this once the player open his map, but he does not have to enter the map to order his units around. If the server is running with higher difficulty, or disabled map icons, I am not even sure it is possible to giver 'getin' orders on the map. Finally, I am not sure what the last line where you waituntil shownMap is for, you already waited for the map to be visible?
  12. My advice is to run this on clients. This also means it will work for all sides. Second, we know that empty vehicles don't move on their own, so we don't need to constantly update. I believe it is practical to go through all vehicles regularly on the client. There are more advanced ways with event handlers, but just wanted to quickly make something. The only possible problem with doing it client side, is that vehicles that are far away might not get transmitted to clients. Anywhere I made this script, and tested it and it seems to work (not sure how well the _visual commands work, maybe just use ordinary): if (!hasInterface) exitWith {}; EM_Vehicles = []; EM_MapDrawCallback = { params ["_mapCtrl"]; // This just draws the data from EM_Vehicles on the map. { _x params ["_obj", "_pos", "_dir", "_icon", "_color"]; _mapCtrl drawIcon [ _icon, _color, _pos, 22, 22, _dir, '', 0, 0.03, 'TahomaB', 'center' ]; } forEach EM_Vehicles; }; [] spawn { disableSerialization; while {true} do { // Wait for player object to determine side waitUntil {sleep (5 + random 3); !isNull player }; // This array links colors, side indexes, with actual side values private _sideArray = [east, west, resistance, civilian]; // We pick the group side, because you can mix members in a group private _playerSide = _sideArray find (side group player); private _alpha = 0.5; // We only need to select the color once per loop, since we only care for one side private _color = [ [1,0,0,_alpha], [0,0,1,_alpha], [0,1,0,_alpha], [1,1,0,_alpha] ] select _playerSide; private _cfgVehicles = configFile >> "CfgVehicles"; private _newVehicles = vehicles select { (_x isKindOf "AllVehicles") and //Just avoid possible weird objects (count units _x == 0) and //Empty (getNumber (_cfgVehicles >> typeOf _x >> "side") == _playerSide) //On the player's side }; // From the vehicle gather the data EM_Vehicles = _newVehicles apply { private _cfg = _cfgVehicles >> typeOf _x; private _icon = getText (_cfg >> "icon"); private _side = getNumber (_cfg >> "side"); [ _x, getPosASLVisual _x, getDirVisual _x, _icon, _color ] }; }; }; // We must wait for the player and display to be active before we can add the event handler. [] spawn { disableSerialization; waitUntil {sleep 0.25; !isNull player}; private _display = objNull; while {isNull _display} do { sleep 0.25; _display = findDisplay 12; }; private _ctrl = _display displayCtrl 51; _ctrl ctrlAddEventHandler ["Draw", EM_MapDrawCallback]; };
  13. You can get the most basic info, like which players are on, current mission, etc, but probably not their game/steam/battleye ids using ordinary steam query. For more, especially if you want to manipulate the server, you might want to look into BattleEye and their RCon, but this not trivial to implement. This might get your started: https://community.bistudio.com/wiki/BattlEye#RCon The protocol and download for an RCon client is here https://www.battleye.com/downloads/ . You might want to just find an existing RCon client and see how to integrate with that or get it to do what you want. Of course, there is tezz' suggestion to write an extension—which would probably be simpler than implementing the protocol above—but if a few server hosts may not allow adding custom DLLs.
  14. You supply the ID in the first argument when you create the task (call _setTask)—so you already know it, just have to embed it in the string.
  15. Well the documentation does say it " Ends the mission properly for all players in a multiplayer environment". Since the endMissionServer graphics is only for players, you might want to try something like this: if (isServer) then { "SideScore" call BIS_fnc_endMissionServer; if (isDedicated) then { // Wait some time before shutting down server so players do not get an early disconnect [] spawn { sleep 30; forceEnd; endMission "END1"; }; }; };
  16. In general you can't change config properties unless they are exposed from a particular script command, like setUnitTrait. It is the case for almost all config properties that you cannot change them unless you modify the addon. I believe you can in VBS, but that won't help you here. It seems from the talk page on Biki - if you look at the bottom - that you are not alone in your request: https://community.bistudio.com/wiki/Talk:setUnitTrait .
  17. There is a very good reason for that. It is done to both avoid leaking local variables, and also not to have them accidentally overridden. The _this variable is local (private) like any other variable, and is accessible like any other. The variable _this is not always nil for a function called with no argument, and doing that kind of nil check will not work for nested calls. Not setting it to an explicit value can make errors harder to diagnose, especially for medium to large codebases. Personally, I consider code that calls like this for broken, if the code is somehow to be used by others, are call others. Here is an example: Fnc_WithOptionalArgument = { // You can use this instead: // params [["_value", 10]] // but the call later is still wrong private _value = if (isNil "_this") then {10} else {_this}; // Do something with value. }; // For some reason, give this unit waypoint on some its nearest alive enemy. [someDude] call { params ["_unit"]; // This function call will not work intended since the // argument is not a number, but rather call Fnc_WithOptionalArgument; // Do something with _unit //.... };
  18. You asked how to do everything manually. Here is one explanation. The typical approach used (at least for me) is to consider two kinds of clients. The ones currently in the game. For those you give updates regularly and they are then always up to date - this is Part 1. The second kind just joined (JIP) and are not up to date. The goal here is to make them up to date like the rest - Part 2 - and then give them the same updates regularly. Here is how to do it without completely vanilla. For Part 1, (could be in init.sqf): // Section 1 - This is essentially your "stat.sqf" except rewritten as a function. You can still do it with a script though. OnTaskStatusChange = { params ["_taskId", "_newStatus"]; if (_taskId == 1) then { // Create task if not existing // ... // Other tasks based on new status for example: if (_newStatus == "SUCCEEDED") then { // Play conversation // Move respawn }; if (_newStatus == "FAILED") then { // Play other conversation }; }; }; // Section 2 if (hasInterface) then { "Task_1_Status" addPublicVariableEventHandler {[1, _this select 1] spawn OnTaskStatusChange;}; }; // Section 3 if (isServer) then { // Initialize to say, "PENDING" Task_1_Status = "PENDING"; publicVariable "Task_1_Status"; }; In your trigger you would then have something like this in your on act: if (isServer) then {Task_1_Status = "SUCCEEDED"; publicVariable "Task_1_Status"; if (hasInterface) then {[1, Task_1_Status] spawn OnTaskStatusChange; };}; The first two pieces update the variable, and gives it to all clients. Their handler then runs, and thus runs OnTaskStatusChange on all their machines. The last expression is needed because if you do publicVariable, the handlers only run on all other machines except the one you public variable on, so you need to do that manually. This takes care of all current clients. But if someone JIPs that won't get the task until the next update. So we modify the previous section 2 to this: // New section 2: if (hasInterface) then { //Either we already got the data, or we have not received any yet. //If we already have it we need to run manually if (!isNil "Task_1_Status") then { [1, Task_1_Status] spawn OnTaskStatusChange; }; //If we did not have the data yet, we can expect it shortly. //Regardless, on any future updates run the function again. "Task_1_Status" addPublicVariableEventHandler {[1, _this select 1] spawn OnTaskStatusChange;}; }; To be honest all this public variable becomes annoying, so I use something like CBA_addEventHandler and CBA_GlobalEvent . Then the on act of the trigger simply becomes: ["Task_Status", [1, "SUCCEEDED"]] call CBA_fnc_globalEvent; And instead of addPublicVariableEventHandler I just do: ["Task_Status", OnTaskStatusChange] call CBA_fnc_addEventHandler;
  19. For the task itself, your second code should be fine, assuming your trigger is something like: ["t1","stat.sqf"] remoteExec ["execVM",0], since the task framework should automatically work both for current and JIPs so nothing to do there. Instead of both your calls to taskSetState, I would do: if (isServer) then {["tsk1", "SUCCEEDED",true] spawn BIS_fnc_taskSetState;}; since the a single call should automatically sync with current players and JIPs. Assuming BIS_fnc_addRespawnPosition is JIP-compatible - since it is global you might want to wrap that in isServer also - you should be done.
  20. Well the trigger itself only activates/has any effect on the server right? The execution of remoteExec command is only done on the server, and it ensures that "task1completed" execVM "stat.sqf" runs for all current clients. If you add the JIP flag, then it will also run for all future JIPs - isn't that what you want? For all future JIPs to see the task status as complete? Edit: looking at Task Framework implementation you could use that, and at appearance it does look like BI actually made a library that is JIP compatible. It makes all the data about tasks available using publicVariables, and also uses a [...] remoteExecCall ["TaskId", 0, true] to ensure JIPs also get the tasks.
  21. Muzzleflash

    Disable randomization

    After looking at it a bit. You can't overwrite the function since it is defined in cfgFunction and is final. There are in fact two places it is randomized, and one of them is run directly, so overwriting was never a choice anyway. If you want to overwrite using CBA you would have to do something like this in description.ext: class Extended_Init_EventHandlers { class PO_ISTS_Infantry_B_Base_IND { init = "_this execVM ""myScript.sqf"";"; }; }; You would have to repeat that for the inner Infantry_Base class for all of them. Your "myScript.sqf" in the mission should then revert the randomization. I think that should work - have done similar things using CBA event handlers before. Only other option is to modify, or make an extension addon, for project OPFOR, but that would mean getting permission and etc.. Best bet may be to ask project OPFOR to add such a feature: "PO_enableRandomization" .
  22. Try adding the JIP parameter, then it should also run for JIPs. ["task1Completed","stat.sqf"] remoteExec ["execVM",0, true];
  23. Muzzleflash

    Disable randomization

    The units from project OPFOR has their own code and has no special handling from BIS_enableRandomization. They run their own function which randomizes outfits. You would have to either overwrite the event handler in the config; or overwrite the entries it uses to pick the equipment; or hijack the randomize function, but I think it is finalized from cfgFunctions so you cannot do that. Interestingly, my own squad is experiencing some weird stuff when a new Zeus joins. Some project opfor units lose their pants. But we run multiple other addons so can't say for sure which one is at fault. Another option if you use CBA, would be to define you own init event handler in description.ext, where you replace their equipment.
  24. Muzzleflash

    EventHanlder being triggered for everyone

    Doesn't matter where it is added since it is added for the (always) local player, and the variable incremented is always the global variable on a single machine. This could only lead to multiple countings if added too many times. But, if you see the friends value, that would suggests the value is somehow being made public (e.g. using publicVariable) and overwriting it on other machines.
  25. Muzzleflash

    Persistent Vehicle Saving

    Depending on what you mean by 3rd party databases, I'm guessing sqlite is also not viable, nor anything else that requires putting a DLL on the server. You can dump info into the .rpt file but that requires .rpt logging is enabled, and only easily handles the saving part. Otherwise your only solution is to use profileNamespace. But for saving the actual data, I guess you can just use the same technique you currently use to save the player positions? Curios to how that is done. For vehicles it is more complicated because you need correlate ids with what you saved. So if you have Vehicle A at mission start and give it ID X, then when you load the mission, you have to somehow find out how to give it ID X again, otherwise you can't update its stats to match what was saved, and you risk spawning another vehicle on top of it. Another option is to distinguish between starting and loading a mission. If the mission is "started" you don't load anything. If the mission was "loaded", you delete everything at the beginning and recreate what is necessary from the save. Other than that, you save stuff pretty much like you do for players.
×