• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[C#] Mapmaking in csharp

Level 5
Joined
Jun 30, 2019
Messages
33
There are two libraries for handling object data, War3Api.Object and War3Net.Build.Core.

The class War3Net.Build.Object.UnitObjectData is an abstraction of the .w3u file which contains the unit object data. There are extension methods on BinaryReader and BinaryWriter to save/load the file.
War3Api.Object is more focused on editing the object data, making it a lot easier to use than the other library for editing unit data.

To save the data from War3Api.Object, you use the method War3Api.Object.ObjectDatabase.GetAllData(), which maps the War3Api.Object objects to the War3Net.Build model, allowing you to save the file.
However, I don't think I implemented a mapping for the other way (yet), which you'll need if you're not creating the unit data from scratch, but want to edit an existing file.

Another approach would be to create the modifications you want in a new .w3u file and then merge it with the existing file, but merging two .w3u files is also not yet implemented.
Awesome, thankyou. It looks like mapping to BaseObject derived classes should already be feasible so I'm going to make an attempt at a pull request.
 
Level 18
Joined
Jan 1, 2018
Messages
728
To anyone using War3Api.Object, I have updated this package to v1.31.1-rc.11, and I have unlisted all previous versions because they were not in correct SemVer format, causing rc.11 to not show up as the latest version.
 
Level 5
Joined
Jun 30, 2019
Messages
33
Hi, me again.
I'm trying to transpile a combination of vJASS, JASS-based GUI, and C# into a single output script. I have successfully used your libraries to transpile vJASS --> Lua and C# --> Lua, but I can't figure out a good way to merge them together coherently. Do you have any recommendations?
 
Level 4
Joined
Mar 8, 2012
Messages
43
Hello again! I had a request to include host detection into WCSharp, as to the best of my knowledge it is still possible to do so in Lua. However, this requires hooking into the code of the map prior to the "main" and "config" code are executed, and currently the CSharp code base only gets initialised at the end of "main" method. For host detection to work, I believe we need to place code behind the definition of config/main that gets executed immediately.

Now I don't really know the logistics behind the InitCSharp method and such, I wager it's at the end of "main" for good reasons, and I also imagine it may be hard if not impossible to let the C# code run before that. All that being said however, I would still like to request for an option to run code earlier than that, even if it has to be written in pure Lua. It may also not be possible therefore to include it into WCSharp as a NuGet package, but if it could be included as a code snippet that people can copy/paste into their own projects that'd still be acceptable, I think.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hello again! I had a request to include host detection into WCSharp, as to the best of my knowledge it is still possible to do so in Lua. However, this requires hooking into the code of the map prior to the "main" and "config" code are executed, and currently the CSharp code base only gets initialised at the end of "main" method. For host detection to work, I believe we need to place code behind the definition of config/main that gets executed immediately.

Now I don't really know the logistics behind the InitCSharp method and such, I wager it's at the end of "main" for good reasons, and I also imagine it may be hard if not impossible to let the C# code run before that. All that being said however, I would still like to request for an option to run code earlier than that, even if it has to be written in pure Lua. It may also not be possible therefore to include it into WCSharp as a NuGet package, but if it could be included as a code snippet that people can copy/paste into their own projects that'd still be acceptable, I think.
It might be possible to run C# code in the config function, however the C# code would then have two entry points, so it'd have to be compiled as a library with those two entry methods having some kind of attribute, instead of compiling as an 'exe' with a Main method. Not sure how it'd work with InitCSharp method though, that's CSharpLua transpiler territory.

The C# code currently gets run after everything else in war3map.lua's main method, to ensure the map is properly initialized (unit creation etc), I guess it could be moved up if you really want to (for example if you want pre-placed units to be registered for a trigger with 'unit enters playable map area' event).

I think the easiest solution would be to do this in lua. By default only the CoreSystem .lua files are passed to the compiler, but you can easily add your own .lua files to that.
 
Level 4
Joined
Mar 8, 2012
Messages
43
It might be possible to run C# code in the config function, however the C# code would then have two entry points, so it'd have to be compiled as a library with those two entry methods having some kind of attribute, instead of compiling as an 'exe' with a Main method. Not sure how it'd work with InitCSharp method though, that's CSharpLua transpiler territory.

The C# code currently gets run after everything else in war3map.lua's main method, to ensure the map is properly initialized (unit creation etc), I guess it could be moved up if you really want to (for example if you want pre-placed units to be registered for a trigger with 'unit enters playable map area' event).

I think the easiest solution would be to do this in lua. By default only the CoreSystem .lua files are passed to the compiler, but you can easily add your own .lua files to that.
That attribute approach does sound like a really good idea! I do not believe it should have to conflict with any of the transpiler functionality.
In that same git repo as before, there is some functionality for hooking to config/main, the way I see it is that War3Net could analyze the source project/dll for attributes like "BeforeConfig" etc. (these can be defined in something like War3Api.Attributes), and just call those at the appropriate times, similar to how it currently calls Source.Program.Main(). This doesn't really need to classify as multiple entry points from the "exe" perspective, I don't believe, so no project changes are required. And I'd like to hope that initialising the C# codebase during config wouldn't break anything on that side anyway. I believe that even static constructors aren't fired until something actually tries to interact with that class (i.e. not when the class is defined), so I believe all problems should be circumventable and any problems should always be caused by the user misusing the attribute.

The Lua-based solution is a valid one as well, but I think I'll see about cloning the War3Api repo again and seeing if I can implement functionality for this on the C# side, as anything that removes Lua from the picture is a win in my book.
 
Level 18
Joined
Jan 1, 2018
Messages
728
That attribute approach does sound like a really good idea! I do not believe it should have to conflict with any of the transpiler functionality.
In that same git repo as before, there is some functionality for hooking to config/main, the way I see it is that War3Net could analyze the source project/dll for attributes like "BeforeConfig" etc. (these can be defined in something like War3Api.Attributes), and just call those at the appropriate times, similar to how it currently calls Source.Program.Main(). This doesn't really need to classify as multiple entry points from the "exe" perspective, I don't believe, so no project changes are required. And I'd like to hope that initialising the C# codebase during config wouldn't break anything on that side anyway. I believe that even static constructors aren't fired until something actually tries to interact with that class (i.e. not when the class is defined), so I believe all problems should be circumventable and any problems should always be caused by the user misusing the attribute.

The Lua-based solution is a valid one as well, but I think I'll see about cloning the War3Api repo again and seeing if I can implement functionality for this on the C# side, as anything that removes Lua from the picture is a win in my book.
I tested moving Program.Main() from InitCSharp to the end of main and then calling InitCSharp at the start of config, it seems like it still works correctly.
However I still see an issue for getting this to work without updating the war3map.lua manually. Currently the InitCSharp method identifier is a const string in CSharpLua. The main function generator uses this const string to create a call statement. The issue here is that the function is generated by the MapScriptBuilder class, which outputs JASS code that gets transpiled to lua. JASS syntax does not support calling something like "Program.Main()", so the transpiler would have to generate a helper method with an identifier name that is compatible with JASS (so without the dots). Then you'd get something like this:

Lua:
local InitCSharp = function ()
  System.init({
    types = {
    ...
    }
  })
end

local mainAfter = function ()
  Program.Main()
end

main = function ()
  ...
  mainAfter()
end

config = function ()
  InitCSharp()
  ...
end
 
Level 1
Joined
Feb 7, 2022
Messages
1
Hey guys,

I'm trying to work out how to add resources (gold) to a player when something happens. I've tried looking around but the only option I can think of is using an ability and having a dummy cast it to give gold. Is there a simpler way to add it to my script? like a GivePlayerGold(player, amount) function?

Thanks :)

edit: SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD, 50); worked for this.
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
Hey guys,

I'm trying to work out how to add resources (gold) to a player when something happens. I've tried looking around but the only option I can think of is using an ability and having a dummy cast it to give gold. Is there a simpler way to add it to my script? like a GivePlayerGold(player, amount) function?

Thanks :)

edit: SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD, 50); worked for this.
This uses the same API as Jass/Lua. This means that you have access to everything that you can do in GUI, which is a good place to check if you're having trouble finding a function:
exa.png

Note that the converted GUI trigger will often use the BJ version of a function. In some cases, a non-BJ version exists which is often more efficient or better in some way so you'll probably want to use that instead. Other times, only a BJ version exists which is the case here (although, SetPlayerState() is basically the same thing).
 
Last edited:
Level 1
Joined
Feb 19, 2022
Messages
2
Hello! Can you help me please:
1) how to create a region?
2) how to create a "unit entered region" event?
 
Last edited:
Level 14
Joined
Aug 31, 2009
Messages
774
Hello! Can you help me please:
1) how to create a region?
2) how to create a "unit entered region" event?
As Uncle mentioned above, you use the standard JASS commands.

If you're using the WCSharp libraries, you can just make regions in the editor, and use the Constant Generator to create Rectangle objects of them, which are easier to work with. The event system can also handle any "enters region" stuff from thereon.
 
Level 1
Joined
Feb 19, 2022
Messages
2
Please write me a small example of an event (on csharp) on a unit entering a region. Thanks.
And how can you do without first creating regions in the map editor? To create regions using csharp.
 
Last edited:
Level 4
Joined
Mar 8, 2012
Messages
43
Hi, I'm having trouble understanding an error I'm getting. The trouble is that I get this error on one computer, but not on another, and I really can't tell what the difference might be.
I suggest confirming your visual studio version and ensuring that you have the same workloads selected in the Visual Studio installer. You should have something like VS2019 v16.11.x with .NET Desktop Development selected.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hi, I'm having trouble understanding an error I'm getting. The trouble is that I get this error on one computer, but not on another, and I really can't tell what the difference might be.
View attachment 398280
Seems like the decompiler library can't find one of the libraries that is used, you could check if it exists in the nuget package cache, which is normally located in "C:\Users\<user>\.nuget\packages\war3api.common\1.32.0".
 
Level 5
Joined
Jun 30, 2019
Messages
33
I suggest confirming your visual studio version and ensuring that you have the same workloads selected in the Visual Studio installer. You should have something like VS2019 v16.11.x with .NET Desktop Development selected.
Yep, good point. I can confirm that both computers are running Visual Studio 2019 16.11 with only .NET Desktop Development enabled.

Seems like the decompiler library can't find one of the libraries that is used, you could check if it exists in the nuget package cache, which is normally located in "C:\Users\<user>\.nuget\packages\war3api.common\1.32.0".
Yep, both War3Api.Common and WarApi.Blizzard were present in the Nuget package cache on the failing computer.

Thanks both for your help. Is there anything else I could try?
 
Level 5
Joined
Jun 30, 2019
Messages
33
Seems like the decompiler library can't find one of the libraries that is used, you could check if it exists in the nuget package cache, which is normally located in "C:\Users\<user>\.nuget\packages\war3api.common\1.32.0".
Hi, some additional information that I failed to notice earlier: my project references 1.32.9, not 1.32.0, and thus both computers only have war3api.common\1.32.9 in the cache. Do you know why the failing computer might be trying to resolve 1.32.0?
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Hi, some additional information that I failed to notice earlier: my project references 1.32.9, not 1.32.0, and thus both computers only have war3api.common\1.32.9 in the cache. Do you know why the failing computer might be trying to resolve 1.32.0?
It's probably because the AssemblyVersion is set to 1.32: War3Api/War3Api.Common.csproj at master · Drake53/War3Api

You might have better luck asking here: Issues · icsharpcode/ILSpy
Make sure to include the version (my CSharpLua fork currently uses 6.2.1.6137), and the stacktrace (which should contain "decompiler.DecompileWholeModuleAsSingleFile()" from CSharp.lua/LuaSyntaxGeneratorFactory.cs at master · Drake53/CSharp.lua).
 
Level 4
Joined
Mar 8, 2012
Messages
43
As of .NET 6, interpolated strings get compiled into a rather convoluted construct, like so:

C#:
// original
return $"{this.saveFolder}\\{GetPlayerName(player)}-save-{saveSlot}{this.suffix}.pld"

// compiled
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 4);
defaultInterpolatedStringHandler.AppendFormatted(saveFolder);
defaultInterpolatedStringHandler.AppendLiteral("\\");
defaultInterpolatedStringHandler.AppendFormatted(Common.GetPlayerName(player));
defaultInterpolatedStringHandler.AppendLiteral("-save-");
defaultInterpolatedStringHandler.AppendFormatted(saveSlot);
defaultInterpolatedStringHandler.AppendFormatted(suffix);
defaultInterpolatedStringHandler.AppendLiteral(".pld");
return defaultInterpolatedStringHandler.ToStringAndClear();

This creates problems with using WCSharp unfortunately, since CSharpLua doesn't implement DefaultInterpolatedStringHandler.
There's basically 3 solutions here and I'm not really sure which is easiest/best or even works.
1. I remove all interpolated strings. For all I know the compiler will just turn my string concatenations into interpolated strings however, and this is a rather arbitrary restriction on all future packages anyway.
2. War3Net prevents decompilation from generating this kind of code, but I imagine that this isn't really in your control.
3. CSharpLua implements DefaultInterpolatedStringHandler, even though this is kind of completely redundant because it is only really used in compiled code and undoubtedly less performant than the old way of handling interpolated strings (i.e. not generating a new object and several method calls for no reason...).

I'll raise the topic on the CSharpLua repo as well, but I figured I should post here as well just in case there is a way out that doesn't involve implementing DefaultInterpolatedStringHandler in CSharpLua.
 
Level 18
Joined
Jan 1, 2018
Messages
728
As of .NET 6, interpolated strings get compiled into a rather convoluted construct
This seems like an issue with the decompiler not reconstructing the optimized code into an interpolated string.
I have updated the decompiler version used to the latest, and updated the War3Net.CSharpLua package to v1.7.20
If this doesn't solve the problem you might want to open an issue in Issues · icsharpcode/ILSpy
 
Level 4
Joined
Mar 8, 2012
Messages
43
This seems like an issue with the decompiler not reconstructing the optimized code into an interpolated string.
I have updated the decompiler version used to the latest, and updated the War3Net.CSharpLua package to v1.7.20
If this doesn't solve the problem you might want to open an issue in Issues · icsharpcode/ILSpy
The problem is still there yes, even after deleting the CSharpLua/packages folder. I suppose I'll raise the issue with ILSpy as well, although it doesn't necessarily seem like unintended behaviour on their side.
 
Level 4
Joined
Mar 8, 2012
Messages
43
Sorry to ask but can you check out the ILSpy issue?

It's hard for me to get a local War3Net build going on my own system. It seems that the desired feature is already implemented in a pre-release anyway, although for whatever reason if I make a small local test (i.e. not within War3Net) the output is different to begin with.

EDIT: Okay nevermind, it does work as expected, just with some weirdness. Either way it seems a pre-release version is required in order to restore Interpolated strings to their desired format.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Sorry to ask but can you check out the ILSpy issue?

It's hard for me to get a local War3Net build going on my own system. It seems that the desired feature is already implemented in a pre-release anyway, although for whatever reason if I make a small local test (i.e. not within War3Net) the output is different to begin with.

EDIT: Okay nevermind, it does work as expected, just with some weirdness. Either way it seems a pre-release version is required in order to restore Interpolated strings to their desired format.
I updated War3Net.CSharpLua to v1.8.0-preview.1, which uses preview3 of the decompiler.
 
Level 5
Joined
Sep 24, 2015
Messages
33
So I set up the entire WCSharpTemplate and everything seems to be working fine, but I'm completely new to C# and I'm not sure if what I'm trying to do is within the scope of this tool.

I have an Excel Workbook to create a calculator for unit stats RPG style based on the values I've used to define their Class, Race, Weapon, Armor, etc.
Once these parameters are input into a new row on a table I've created, they will populate the rest of that row with stats that I can manually input into the Object Editor for the unit's health, mana, strength, agility, intellect, damage dice, etc. They're not all perfect and some values need tweaking, but I am tired of manually updating all my units anytime I tweak a value somewhere in the formulas or some data in the worksheets for Race, Class, etc.

I was thinking the WCSharp Constant Generator would grab all the custom unit data from every custom unit I have (they all have new names), so I could then create some functions and variables based on my Excel Workbook that could be used to create a new Object Editor import file that I could use in my map instead of manually inputting it all and updating every time I change a small variable.
 

Attachments

  • Warcraft 3 Sandbox Map Formulas.xlsx
    126.9 KB · Views: 2
Level 18
Joined
Jan 1, 2018
Messages
728
So I set up the entire WCSharpTemplate and everything seems to be working fine, but I'm completely new to C# and I'm not sure if what I'm trying to do is within the scope of this tool.

I have an Excel Workbook to create a calculator for unit stats RPG style based on the values I've used to define their Class, Race, Weapon, Armor, etc.
Once these parameters are input into a new row on a table I've created, they will populate the rest of that row with stats that I can manually input into the Object Editor for the unit's health, mana, strength, agility, intellect, damage dice, etc. They're not all perfect and some values need tweaking, but I am tired of manually updating all my units anytime I tweak a value somewhere in the formulas or some data in the worksheets for Race, Class, etc.

I was thinking the WCSharp Constant Generator would grab all the custom unit data from every custom unit I have (they all have new names), so I could then create some functions and variables based on my Excel Workbook that could be used to create a new Object Editor import file that I could use in my map instead of manually inputting it all and updating every time I change a small variable.
Not sure I 100% understand what you're trying to do, but it seems you're trying to map data from an excel file to object data files (like war3map.w3u).

The game already supports .slk files (like units/unitdata.slk), which can be opened with excel. If you want to use that you have to make sure to save the .slk in that exact format. I think there already exist tools that can convert the .slk back to .w3u/etc.

Alternatively if you save the excel in your own format, then you probably have to use something like this to parse it: GitHub - ExcelDataReader/ExcelDataReader: Lightweight and fast library written in C# for reading Microsoft Excel files
Then you can write your own code to convert it to the .w3u/etc files. You can use War3Net.Build.Core's ObjectData types for this.

There's also War3Api.Object that's basically a more user-friendly wrapper around War3Net.Build.Core's object data types, but that's mainly intended for when you manually set the object properties in code. If you do this automatically through a conversion then it may not be needed to use it.

I'm not really familiar with the WCSharp constant generator, but that seems more useful if you want to convert the other way (from war3map to excel). Might be useful to get the initial excel file set up which you can then edit.
 

rom

rom

Level 1
Joined
Feb 6, 2023
Messages
1
There's also War3Api.Object that's basically a more user-friendly wrapper around War3Net.Build.Core's object data types, but that's mainly intended for when you manually set the object properties in code.

Do you have an example? I'd really like to define my units completely in code -- or at least tweak properties. I'm having trouble getting SimpleObjectDataModification to work at all.

Update: Using a new base map saved with the latest version of the editor seems to have helped, this works as expected:

C#:
var simpleObjectModification = new SimpleObjectModification
{
    OldId = "ndkw".FromRawcode(),
    NewId = "9999".FromRawcode(),
    Unk = new List<int>
    {
        0
    },
    Modifications = new List<SimpleObjectDataModification>
    {
        new SimpleObjectDataModification
        {
            Id = "unam".FromRawcode(),
            Type = ObjectDataType.String,
            Value = "Test"
        },
        new SimpleObjectDataModification
        {
            Id = "usca".FromRawcode(),
            Type = ObjectDataType.Real,
            Value = 2.0f
        }
    }
};
map.UnitObjectData.NewUnits.Add(simpleObjectModification);
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Do you have an example? I'd really like to define my units completely in code -- or at least tweak properties. I'm having trouble getting SimpleObjectDataModification to work at all.

Update: Using a new base map saved with the latest version of the editor seems to have helped, this works as expected:

C#:
var simpleObjectModification = new SimpleObjectModification
{
    OldId = "ndkw".FromRawcode(),
    NewId = "9999".FromRawcode(),
    Unk = new List<int>
    {
        0
    },
    Modifications = new List<SimpleObjectDataModification>
    {
        new SimpleObjectDataModification
        {
            Id = "unam".FromRawcode(),
            Type = ObjectDataType.String,
            Value = "Test"
        },
        new SimpleObjectDataModification
        {
            Id = "usca".FromRawcode(),
            Type = ObjectDataType.Real,
            Value = 2.0f
        }
    }
};
map.UnitObjectData.NewUnits.Add(simpleObjectModification);
Maybe this helps: What options exist for programmatically manipulating Object Editor data, anno domini 2023?
 
Level 1
Joined
Mar 30, 2023
Messages
1
Hello everybody! I've been using this C# resource for a couple of weeks now and have been loving it so far. While building and testing a save/load system, I ran into an interesting issue. Saving and loading work the majority of the time, but at random levels, the save fails to load from file.

LoadFromFileError.png


It seems to be an issue with the way the save file text is generated or read from file, and only with certain values. This error looks to be within the functionality of the WCSharp.SaveLoad package.

Has anybody encountered this issue before or have advice on what might be going on and possible solutions? All help is appreciated.

Code:
function PreloadFiles takes nothing returns nothing

    call PreloadStart()
    call Preload( "" )
call BlzSetAbilityTooltip(1097690227, "eyJTYXZlU3RyaW5nIjoie1wiSGVyb1NhdmVEYXRhXCI6e1wiSWRcIjoxMjExMTE3NjE4LFwiSW50ZWxsaWdlbmNlXCI6MjgsXCJHb2xkXCI6NDEsXCJMdW1iZXJcIjowLFwiWHBcIjo5MzksXCJMZXZlbFwiOjQsXCJTdHJlbmd0aFwiOjIyLFwiQ", 0)//" )
    call Preload( "" )
call BlzSetAbilityTooltip(1098018659, "WdpbGl0eVwiOjE4fSxcIklzUG9wdWxhdGVkXCI6dHJ1ZX0iLCJWZXJzaW9uIjoyLCJIYXNoQ29kZSI6MTM0MDQ3NjI5OX0=", 0)//" )
    call Preload( "" )
endfunction
function a takes nothing returns nothing
 //" )
    call PreloadEnd( 0.0 )

endfunction

Code:
function PreloadFiles takes nothing returns nothing

    call PreloadStart()
    call Preload( "" )
call BlzSetAbilityTooltip(1097690227, "eyJTYXZlU3RyaW5nIjoie1wiSGVyb1NhdmVEYXRhXCI6e1wiSWRcIjoxMjExMTE3NjE4LFwiSW50ZWxsaWdlbmNlXCI6MzEsXCJHb2xkXCI6NTksXCJMdW1iZXJcIjowLFwiWHBcIjoxNTAwLFwiTGV2ZWxcIjo1LFwiU3RyZW5ndGhcIjoyNCxcI", 0)//" )
    call Preload( "" )
call BlzSetAbilityTooltip(1098018659, "kFnaWxpdHlcIjoxOX0sXCJJc1BvcHVsYXRlZFwiOnRydWV9IiwiVmVyc2lvbiI6MiwiSGFzaENvZGUiOjY1NjgyMzM1NX0=", 0)//" )
    call Preload( "" )
endfunction
function a takes nothing returns nothing
 //" )
    call PreloadEnd( 0.0 )

endfunction
 
Last edited:
Level 4
Joined
Mar 8, 2012
Messages
43
I'll just raise this topic in the off-chance that you know anything, as well as just getting future advice out there for some of these issues that others may come to face as well.

Me and Damage have been suffering from desync issues within our map for quite a while now. We've identified some potential issues, and have definitely reduced the frequency of desyncs, but unfortunately the issue continues to plague the map and at this point getting a single desync to occur could take a handful of games. Frequent enough to make people not want to play the map, but infrequent enough to make testing whether a change helped an extremely tedious process, even when trying our best by doing a sort of binary-search style of enabling/disabling functionality.

It's worth noting that Damage's other large C# map, Assault the Throne, has not had any issues with desyncs, but as a C# programmer IRL, I bring some more advanced techniques and code to the table and have inadvertently brought problems along with me.

One of the things that we've at the very least noted as a guaranteed source of desyncs is sorting. Although this may be obvious to someone experienced in Lua, I personally expected that the sorting would be deterministic, as most sorting algorithms are. But Lua's is not, meaning that any instance of using OrderBy without being 100% sure that there is only ONE possible order is a risk. The simple solution here is to use a ThenBy to sort by something unique such as an ID to ensure the list has only one possible sort order.

More obvious ones are Dictionaries and HashSets, as these aren't stable in C# either. You need to properly sort them before enumerating any of their content, though of course just doing stuff like Contains or TryGetValue does not pose any risks.
Notable is that this extends to some aspects that may not be obvious. For example, the possible values of an enum are stored in a dictionary-like construct, which means that Enum.GetValues will return values in a non-stable order. A list of stuff that I know for sure:
  • Enum.GetValues
  • Enum.GetNames
  • Type.GetMethods (only when you have metadata disabled?)
  • Type.GetFields (only when you have metadata disabled?)
  • Type.GetMembers (inherits from the 2 above ones)

It'd be good to address these non-obvious ones so that mapmakers don't have to concern themselves with these, really. I don't really know about the Reflection ones, but at least Enum does normally have a fixed order to them. If there is a fixed order in C# we can open tickets for CSharpLua itself, but otherwise I'd like to eventually see these things addressed on War3Net's fork.


Anyway, returning to the present, would you perhaps have any ideas on other desync possibilities or solutions? I know that the use of "pairs" is not recommended, while it is used in various places in the CSharpLua code. However, besides the ones listed above, I do not believe that other instances of it in CSharpLua are harmful, as the order should not impact the outcome. Going by Damage's map, we can at least assume that the fundamentals aren't inherently problematic, but there are definitely some hidden traps.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
I can't find a tool to convert .w3u or .w3o to slk and back.
 
Level 5
Joined
Sep 24, 2015
Messages
33
As near as I can tell, that tool converts maps themselves between Lni, Obj and Slk formats. I downlaoded it, and it just immediately crashes, even after quite a bit of troubleshooting, but I don't think it's the tool I need.

I need a tool that converts the object editor's unit data to something I can import into excel (now google sheets since it's cheaper).

If I can convert it into a format like XML or some other easy-to-access data format, I can then create a simple Java program to do whatever I'd want an excel formula to do.

Bottom line, is there any tool anywhere for reading/writing object data outside the editor in a way I can easily manipulate with code or formulas.
 
Top