• 🏆 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
Mar 8, 2012
Messages
44
Alright, I've confirmed that the WCSharp package can at least be imported and compiled now when added via NuGet, but the crux of the problem doesn't really seem to be fixed. I tried messing around with some csproj settings, but in the end I could not prevent WCSharp.Utils from being added to the WCSharp package. However, by breaking the dependency via the settings, it means that WCSharp.Utils is no longer imported when adding WCSharp, and since it's included in WCSharp anyway, you're not missing anything, and there's no duplicate definition error.

So based on this, standalone libraries shouldn't be an issue, but libraries that depend on other libraries cannot be done (outside of the edge case of that other library not being referenced by any other libraries in use anyway).
 
Level 1
Joined
Oct 3, 2020
Messages
2
Hello guys, I want to say that this project is amazing and gave me the final push I needed to try making a map of my own, but I am having some problems that I hope you can help me solve:
1- When I add the War3Api.Object NuGet package, I get the following error when building the map

upload_2020-10-3_11-56-33.png

If I manually install that version of that package dependency it keeps saying the same for other packages. I even tried an empty project with the latest template and installed the War3Api.Object NuGet package but I keep getting the same error.

2- If I want to use an already existing code in Lua, how can I integrate that code in my map and use it from C#? I couldn't find any information about this.

Thank you for your time.
 
Level 5
Joined
Mar 8, 2012
Messages
44
I'm fairly certain that War3Api.Object is a placeholder project of sorts? Both the source repository and nuget package appear empty to me. Either way I haven't been using it in my own projects, so I recommend leaving it out if it's causing issues for you.

As for #2, I believe you can use this bit yanghuan/CSharp.lua to directly perform Lua code instead of having it transpiled. But I don't think the build process currently retains any existing trigger code in the map. So if you want to use this, I'm pretty sure you have to start your trigger code from scratch. I may be wrong though, you'll need an answer from Drake53 to be sure.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
1- When I add the War3Api.Object NuGet package, I get the following error when building the map
I tried to reproduce this in one of my maps that uses this package as well, and I get a build error when using the latest version, but on an older version it still works:
C#:
  <ItemGroup>
    <PackageReference Include="War3Api.Object" Version="1.31.1-rc4" />
    <PackageReference Include="War3Net.Build" Version="1.3.3" />
    <PackageReference Include="War3Net.CSharpLua.CoreSystem" Version="1.2.2" />
  </ItemGroup>

Not sure why you'd get that exception though. Do you have any other packages besides the above three? If not, try with these specific versions first to see if it works then.
EDIT: I should mention that these package references are for the launcher project, so you should not add a reference to War3Api.Object in the source project.

2- If I want to use an already existing code in Lua, how can I integrate that code in my map and use it from C#? I couldn't find any information about this.
You can include lua files in your map by passing them to the ScriptCompilerOptions object here: Drake53/War3Map.Template
By default only the CoreSystem files get added, but you can Append/Concat your own lua file(s).
To use the lua code in C# you must then define the API using @CSharpLua.Template, example: Drake53/War3Api

EDIT2: I have updated War3Api.Object to rc5, fixing an issue caused by the War3Net.Build/War3Net.Build.Core split, so now it can be used with the latest version of War3Net.Build (1.4.0, CoreSystem 1.2.5).
 
Last edited:
Level 1
Joined
Oct 3, 2020
Messages
2
EDIT: I should mention that these package references are for the launcher project, so you should not add a reference to War3Api.Object in the source project.
I see. I misunderstood the purpose of this package. I gave another look to it and I understand now why you say this. Thanks.
BTW, I tried the latest version in the source project (before understanding why I shouldn't) and it throwed me the error in the image. In the launcher project works fine, though.
upload_2020-10-6_19-21-40.png


You can include lua files in your map by passing them to the ScriptCompilerOptions object here: Drake53/War3Map.Template
This is exactly what I was looking for. And here I thought that I looked everywhere. Guess not.

Thank you again.
 
Level 18
Joined
Jan 1, 2018
Messages
728
I have just uploaded War3Net.Build v1.5.0, which is mostly changes for War3Net.Build.Core (updates for war3map.w3i and war3map.wtg).
Though this update doesn't add much for C# mapmaking, I still wanted to mention it here since for convenience my entire War3Net library now targets .NET Core instead of .NET Standard.

Next month .NET 5.0 will release, which 'merges' .NET Core and .NET Standard. The author of the CSharpLua transpiler has already updated to a RC (release candidate) version of .NET 5.0 in order to support new C#9.0 features.
When .NET 5.0 releases my library will also be updated to target it, and I will update my fork of CSharpLua as well. v1.5.0 will probably be the last version before all this happens.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Updated CSharpLua, CSharpLua.CoreSystem, and War3Net.Build to v1.7.0, allowing you to use C#9 features. I'm not sure all the new features have already been implemented by the transpiler, but judging by the commits, you should at least be able to use records, target-typed new expressions, and improved pattern matching.
All new features can be found here: C# 9.0 on the record | .NET Blog
 
Level 14
Joined
Aug 31, 2009
Messages
775
Hello again Drake.

So I remember you added the hidden function UnitAlive(...) and was wondering if you could perhaps add some of the other hidden functions in too. For example, GetUnitGoldCost(...) and all the other hidden things that are typically only used in the common.ai file (cipherxof/war3-types).

I know that to use these functions in a GUI Map, you had to edit the header - but you've managed to do it with UnitAlive, so perhaps you can do it with those too?

I know some of these functions aren't actually useful in anyway, but there's some really really handy ones in there like GetUnitGoldCost(...)
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Hello again Drake.

So I remember you added the hidden function UnitAlive(...) and was wondering if you could perhaps add some of the other hidden functions in too. For example, GetUnitGoldCost(...) and all the other hidden things that are typically only used in the common.ai file (cipherxof/war3-types).

I know that to use these functions in a GUI Map, you had to edit the header - but you've managed to do it with UnitAlive, so perhaps you can do it with those too?

I know some of these functions aren't actually useful in anyway, but there's some really really handy ones in there like GetUnitGoldCost(...)
Adding these functions is easily achieved in the same way any lua variable is accessed through C# code, you only need to declare it using @CSharpLua.Template like done here: Drake53/War3Api
C#:
/// @CSharpLua.Template = "GetUnitGoldCost({0})"
public static extern int GetUnitGoldCost(int unitid);

I don't intend to add any more of the common.ai natives to the API, since other than UnitAlive I'm not sure which ones are useful.
 
Level 14
Joined
Aug 31, 2009
Messages
775
Honestly, the ones that seem actually useful are:

GetPlayerUnitCount
GetUnitGoldCost
GetUnitWoodCost
GetUnitBuildTime
UnitAlive (already added)
UnitInvis

The first one technically isn't required, as that can be done by the standard natives, but things like GetUnitGoldCost or UnitInvis cannot be achieved at all using the common.j natives.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Hello again! I've noticed some strange behaviour surrounding how WC3 seems to handle large numbers. You can retrieve the current date with just typical DateTime.Now, however trying to get actual properties of this such as date.Year will result in an exception on the following line regarding 'x' not having a valid integer representation:
Code:
  function System.div(x, y) if x ~ y < 0 then return -(-x // y) end return x // y end
From what I've been able to tell from compiling ordinary programs with CSharp.lua, this code should work just fine on both 5.1 and 5.3, meaning the problem is to my knowledge not in CSharp.lua but either on WC3's end, or the fork that you have of CSharp.lua (though as far as I could tell the fork should work fine on at least 5.3 too).

Note that retrieving the Ticks property works just fine. DateTime.Now works as expected, it's just that it runs into an error when actually trying to divide numbers as large as DateTime.Ticks, and potentially other operations. In theory you can still retrieve the date by just constructing DateTimes manually in some guided manner and comparing them with DateTime.Now to narrow down what the date is. As long as you don't try to divide Ticks by anything, you don't run into problems. And yes I'm aware of how problematic DateTime is with regards to desyncs and such, but these issues point to a larger issue that I think warrants further attention.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Hello again! I've noticed some strange behaviour surrounding how WC3 seems to handle large numbers. You can retrieve the current date with just typical DateTime.Now, however trying to get actual properties of this such as date.Year will result in an exception on the following line regarding 'x' not having a valid integer representation:
Code:
  function System.div(x, y) if x ~ y < 0 then return -(-x // y) end return x // y end
From what I've been able to tell from compiling ordinary programs with CSharp.lua, this code should work just fine on both 5.1 and 5.3, meaning the problem is to my knowledge not in CSharp.lua but either on WC3's end, or the fork that you have of CSharp.lua (though as far as I could tell the fork should work fine on at least 5.3 too).

Note that retrieving the Ticks property works just fine. DateTime.Now works as expected, it's just that it runs into an error when actually trying to divide numbers as large as DateTime.Ticks, and potentially other operations. In theory you can still retrieve the date by just constructing DateTimes manually in some guided manner and comparing them with DateTime.Now to narrow down what the date is. As long as you don't try to divide Ticks by anything, you don't run into problems. And yes I'm aware of how problematic DateTime is with regards to desyncs and such, but these issues point to a larger issue that I think warrants further attention.
I'm 99% sure this is caused by wc3 running lua in 32-bit (even when using the 64-bit wc3 exe). You probably noticed that dateTime.Ticks is a long, so 64-bit. I'm assuming the Ticks property itself works because the number is converted to a float, but for Year it tries to use integer division, which is not possible since Ticks is too large to store in 32 bits.
Another issue with wc3 running lua in 32-bit is the Year 2038 problem - Wikipedia
I forgot if I fixed this being an issue in the lua CoreSystem library (it's annoying to test since visual studio will nag about the license when you set your local date to the year 2050), but if I didn't, all C# maps will become unplayable in two decades XD (unless by that time the map is rebuilt with some fix, or wc3 uses 64-bit lua by that time, though the latter obviously won't help if you're still on 1.31).
 
Level 5
Joined
Mar 8, 2012
Messages
44
Ah no, I see now that os.time() doesn't return nanoseconds. I thought that 64-bit lua also returned nanoseconds instead of seconds, in addition to its support of 64-bit integers.
I made the assumption that that would mean the DateTime struct works if you remove all instances of '000000' from DateTime and TimeSpan, and that seems to hold true from testing. You would also have to alter the behaviour of MinValue being 1/1/1 instead of 1970/1/1, and probably make Ticks and Milliseconds just throw an exception for clarity. Maybe more?
Would this be something that you would consider? I can understand if this goes beyond the scope that you're interested in, and frankly working with DateTime in WC3 is risky business anyway, so it's probably not good to make it too easy for people to do so. Either way I'd like to make behaviour that will actually sync the times among players and use some resolution algorithm to determine the actual time, and I'd just like to know whether I should bother implementing my own date classes for this.

EDIT: Actually, upon further consideration, given the caveats that would come with such an edited version of DateTime/TimeSpan, I think it's not smart of me to use them even if they were modified to work correctly. As such, nevermind the question. I'll make my own functionality for this from scratch.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Honestly, the ones that seem actually useful are:

GetPlayerUnitCount
GetUnitGoldCost
GetUnitWoodCost
GetUnitBuildTime
UnitAlive (already added)
UnitInvis

The first one technically isn't required, as that can be done by the standard natives, but things like GetUnitGoldCost or UnitInvis cannot be achieved at all using the common.j natives.
Sorry for the late response, I have added these now (I assume you meant GetPlayerUnitTypeCount).
I have created two new packages (War3Api.Common.Legacy and War3Api.Blizzard.Legacy) to make it easier to maintain and so visual studio will stop telling you to update those packages to 1.32.
I also updated the CSharpLua packages with a couple of fixes from the main repo.
The latest versions can be found in the first post.
 
Level 4
Joined
May 18, 2018
Messages
38
Is there an alternative or a way to make ConditionalWeakTable work? As soon as I add one to my map it won't start any more. Guessing it's an issue with the transpiler? (or me being stupid)

Also I don't know if this is a dumb question, but I was wondering about War3Map.Template having a test project. As soon as I create a test that uses War3Api.common (which practically everything in the map uses) I get a method GetUnitCount has no implementation error, which I assume you'll get with every single method in the library? Since they're all missing their implementation. Maybe I just lack the knowledge to make it work though.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Is there an alternative or a way to make ConditionalWeakTable work? As soon as I add one to my map it won't start any more. Guessing it's an issue with the transpiler?
The transpiler uses a built-in library for basic C# types (like lists and vectors), and to handle namespaces/classes: Drake53/CSharp.lua
If you want to use additional system classes, you either need to find the source code for it and transpile (though I think it's unlikely this'll work for a compilerservices class), ask the transpiler author to implement it (yanghuan/CSharp.lua), or use an alternative library (either in C# or lua).

I was wondering about War3Map.Template having a test project. As soon as I create a test that uses War3Api.common (which practically everything in the map uses) I get a method GetUnitCount has no implementation error, which I assume you'll get with every single method in the library? Since they're all missing their implementation.
The test project should probably only reference the common project, not the source project. Although the common project also references War3Api (it actually shouldn't when I think about it).
So yeah the exception is expected since it's a lua API only, when you try to run it in C# you get this.

I have been working on a C# implementation of War3Api.Common (Drake53/War3Net) with which you should be able to properly test your code, but it's a lot of work and I also have other projects I'm working on so don't expect any updates on this project soon.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Hello again.
I'd like to request access to more CSharp.lua compile options, specifically the metadata option. As far as I can tell this is not currently accessible at least.
Secondly I'm wondering if you've given library support more thought. Unless you secretly snuck a fix in while I wasn't looking, it still isn't possible to have NuGet package libraries reference one another (since this results in duplicate code definitions), and it's starting to get a problem for me. Some of my systems rely on each other, and as such I've opted to just package everything together for now. But this becomes a bit of a problem when I want to introduce breaking changes for one of them, as it means anyone interested in using my systems has to either accept being versions behind on everything, or deal with breaking changes. I can try and keep up with backwards compatibility as much as possible, but e.g. for the save/load system overhaul that I'd like to do (which I want the metadata for), it doesn't really make sense to go out of my way to retain backwards compatibility.
 
Level 18
Joined
Jan 1, 2018
Messages
728
@Arxos Let me start with saying, I've been neglecting War3Net.Build lately. I planned to rewrite things from scratch to better support certain use-cases (the main problem is that everything has to be done through a single method 'Build()', which is nice if you want a 'it just works' solution, but it becomes annoying when you need more control over the process and have to dump dozens of setting properties in the ScriptCompilerOptions class), but let's just say my priorities shifted half-way so I got a bunch of unfinished stuff right now. You can tell by the fact that I updated some of my other War3Net packages to 5.x (for .NET 5.0), while War3Net.Build is stuck on 1.7.x.

Now on to your questions.

I'd like to request access to more CSharp.lua compile options, specifically the metadata option. As far as I can tell this is not currently accessible at least.
Can I ask what you need this option for? I'd like to know if it has priority or if you can wait for the v5.x release (which will take at least two more weeks).

Secondly I'm wondering if you've given library support more thought. Unless you secretly snuck a fix in while I wasn't looking, it still isn't possible to have NuGet package libraries reference one another (since this results in duplicate code definitions), and it's starting to get a problem for me. Some of my systems rely on each other, and as such I've opted to just package everything together for now. But this becomes a bit of a problem when I want to introduce breaking changes for one of them, as it means anyone interested in using my systems has to either accept being versions behind on everything, or deal with breaking changes. I can try and keep up with backwards compatibility as much as possible, but e.g. for the save/load system overhaul that I'd like to do (which I want the metadata for), it doesn't really make sense to go out of my way to retain backwards compatibility.
I think the best solution is using the decompile feature which I still haven't fixed. Packaging source code is so uncommon that it's simply impossible to find a solution for the issue.
The solution I had in mind to fix decompiling packages is to use a whitelist and/or blacklist. By default "System.*" packages would be blacklisted.
I also thought of a third option, to use git submodules and project references instead of packages, though I feel like this might run into the same issue as source code packages when you have multiple dependencies depend on some shared base library, and it's also less convenient than packages for end-users.
I'd like to hear your thoughts on what would be the best solution here.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Can I ask what you need this option for? I'd like to know if it has priority or if you can wait for the v5.x release (which will take at least two more weeks).
I'm a bit sporadic in when I work on what, and while I am currently pretty motivated, I think it's fine to wait. There's other things I need to do anyway, so I'll just prioritise them a bit higher in the mean time.
As for the reason, part of the save/load system that I wrote can optionally use some reflection to initialize a class given via generics, and then set the properties according to the saved data. Since metadata wasn't available, I used some hacky solution of enforcing naming of the properties in a certain way in order to figure out the types. I was investigating into ways of making it easier to work with, but stumbled around a lot due to sporadic presence of metadata being available or not. I discovered today that much more metadata should be available, but is not included in the War3Net compilation procedure.

Some of the things that I'd like to do with this is set up a simple json parser (I'll just plug a Lua library for this) that can take a json string input and initialise it into a concrete class, and vice-versa. With metadata, I'm fairly confident I can fully support nesting of things like classes and lists within that class. I can then put this to use to create a more elegant save/load system. Additionally, besides the save/load system there is another system that I want to create that uses sync messages, so I figured I'd add a sync library that automatically sends class-based messages back and forth using the json conversions, and fires appropriate events, in order to greatly simplify the process of syncing data for any use case.

And well, all of this relies on actually being able to make a nice & easy json converter, which I do not think is possible with the current availability of metadata (outside of writing like 90% of the system in Lua anyway).

I'd like to hear your thoughts on what would be the best solution here.
I hadn't really looked too deeply into the code, besides identifying what does and doesn't work on my end. What I will say is that I think git submodules and project references are probably a bad idea. They're really unfriendly to people not well acquainted with Git, and I think there's actually a lot of folks that are fairly new to proper programming or C# that are interested in this project (Damage for one has been recruiting people into using C#/my libraries from all over the place).

It certainly seems like the decompilation option is friendliest to users (and heck even library makers, as getting the project settings right on my end wasn't that easy either), but I don't really know just how hard that'd be to solve? If you can point me in the right direction I may be able to help you a little.
 
Level 18
Joined
Jan 1, 2018
Messages
728
@Arxos I agree decompiled packages are the most user-friendly for both parties involved, so I'll make sure to fix it in the next release.

I'm still thinking about making a 1.7.x 'hotfix' for your metadata issue though. Like I said v5.x will be pretty much remade from scratch, and I'm not even sure yet if I will keep/re-implement the MapBuilder and ScriptCompilerOptions classes (marked with ObsoleteAttribute obviously) to make migrating to the new version easier, or just get rid of them, since even if I add them back I'll have to get rid of them again at some point.



I guess now would also be a good time to ask for some feedback about the new build API I have in mind. Basically it will use the new Map class (which I already implemented here: Drake53/War3Net) with some extension methods that have yet to be implemented (the main functionality that MapBuilder has is adding files from directories, compiling the script, and building the map, so those three will get corresponding extension methods at least). Added files will also be able to set the corresponding property if it's a war3map file.

So I guess it'd look something like this:
C#:
// v1.7 (old)
var mapBuilder = new MapBuilder(...);
var options = new ScriptCompilerOptions(...);

var buildResult = mapBuilder.Build(options, directories);
if (buildResult.Success)
{
    ...
}

// v5.0 (new)
var map = Map.Open(path);
map.AddFiles(directories);

var compilerOptions = new CompilerOptions(...);
var compileResult = map.CompileScript(compilerOptions); // sets map.Script if it succeeds
if (compileResult.Success)
{
    map.Build();
    ...
}
 
Level 5
Joined
Mar 8, 2012
Messages
44
That looks good! It's a more incremental process of constructing the map, which I think will be easier for people to wrap their head around. I believe the idea is (something resembling) a fluent API? If that is indeed the idea you may want to add separate extension methods for each file, although I suppose that may make parsing a pain.

Although speaking from experience with the current process, a lot of people do just opt to copy over a template (such as the one linked on the repo of my library) and not touch the launcher code too much or at all. In the future it may see more use as the object API and such become more powerful, meaning you'd have to fiddle around with the build process a bit, but I think you're probably dealing with advanced users either way in those cases.

Speaking of things in that trend, I do believe you were also aiming to create a more native C# WC3 API in the future yourself? I had actually done some exploring into that area myself to see the feasibility. By wrapping things like "unit" in a proper C# class, you'd be able to easily update values such as life or movement speed of the unit via properties, and have a more central place for unit-related methods. I've mostly focused on libraries to help people easily make cool stuff in their maps, and in a way a native C# doesn't really help with that (since default is fine), but I love the elegance of just being able to modify things via properties and methods, and in general provide a more concise API instead of a set of several hundred global methods.
So anyway, I'm pretty interested in it, although it'd certainly be a lot of work. For that reason it'd probably be a good idea to kind of coordinate this, assuming you have any interest in doing this in the near future.
 
Level 18
Joined
Jan 1, 2018
Messages
728
That looks good! It's a more incremental process of constructing the map, which I think will be easier for people to wrap their head around. I believe the idea is (something resembling) a fluent API?
It was not really meant to be fluent, but it's certainly a possibility since adjusting it to be fluent wouldn't be too much work.

If that is indeed the idea you may want to add separate extension methods for each file, although I suppose that may make parsing a pain.
You mean something like this?
C#:
public static Map SetInfo(this Map map, MapInfo info)
{
    map.Info = info;
    return map;
}

Speaking of things in that trend, I do believe you were also aiming to create a more native C# WC3 API in the future yourself?
By native C# do you mean it would be object-oriented instead of static? While it would be nice to have, I don't enough free time to invest in a feature like this. Since this is basically about replacing War3Api, my first priority in that regard would be to implement War3Net.Runtime.Api, since that would also allow running unit tests in C#. The main differences with War3Api would be that methods have a C# implementation so they are no longer extern, and they would be in different classes instead of dumping everything in a single class, but the entire API would still be static.

I had actually done some exploring into that area myself to see the feasibility. By wrapping things like "unit" in a proper C# class, you'd be able to easily update values such as life or movement speed of the unit via properties, and have a more central place for unit-related methods.
Have you managed to make a proof-of-concept for this? I don't really support this in War3Api because the classes are 'sealed' by making the constructor internal, but if I made those public you could create your own subclasses with properties. Right now the best you can do I think is extension methods and prefixing them with Get/Set if it's for a property.
Maybe in a future version of C# it will be possible to write extension properties, but this feature keeps getting postponed (was supposed to be added in C#8 I think).
 
Level 5
Joined
Mar 8, 2012
Messages
44
You mean something like this?

Yep, exactly. I know it's used more frequently in a lot of web related libraries, including ASP.NET. I don't think it's necessary, but the style is similar to what you initially posted.

Have you managed to make a proof-of-concept for this? I don't really support this in War3Api because the classes are 'sealed' by making the constructor internal, but if I made those public you could create your own subclasses with properties. Right now the best you can do I think is extension methods and prefixing them with Get/Set if it's for a property.

Well you can do a lot with just implicit casts. The main problem is the return types, as it'd be a bother if things still return the WC3 handle types instead of the class objects. For that though, there's nothing stopping me from making a package that fulfills all functionality present in the War3Api, but returning the appropriate objects instead. Then you can just use that instead.

I think the biggest hurdle in the end is that libraries that return WC3 handle types become a bother to work with if you're using the class-based API. Having two sets of not-really-conflicting but annoying-to-use-in-conjunction API's of which any given library may or may not support one of the variants would be pretty annoying. That's probably the biggest reason for me to not bother, but that being said... I'm also currently the only person to have made libraries for this (to my knowledge), so kinda the option is still open for me.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Well you can do a lot with just implicit casts. The main problem is the return types, as it'd be a bother if things still return the WC3 handle types instead of the class objects. For that though, there's nothing stopping me from making a package that fulfills all functionality present in the War3Api, but returning the appropriate objects instead. Then you can just use that instead.
It's probably best to not use War3Api as a base if you make your own API. There's nothing special about it anyways, just some classes, fields, and methods autogenerated with some CSharpLua attributes. The only thing you have to keep in mind when making your own API is that currently the expected namespace for Wc3 types is War3Api.Common: Drake53/CSharp.lua
The file I linked is required in order to be able to create arrays etc of Wc3 types, though it should not be needed anymore if you use the package decompile option in the future (when I fix it).

I'm also currently the only person to have made libraries for this (to my knowledge)
Actually I do know of one other: AzuStar/NoxRaven
Never had time to check it out though, so I don't know what stuff has been implemented there.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Apologies for the hassle but I decided to recreate the WCSharp repository to clean up some history. Since I noticed you had created a fork, I'd like to request that you delete the old one.

Also, random question, but is there a connection between those definitions and the fact that WC3 doesn't seem to like using the WC3 types as indexes? For a while me and Damage had some dictionaries that mapped player objects to an actual class containing more info on that player, but then after a while it started throwing a hissy fit and crashing since it wasn't allowed (despite working fine prior). We kind of assumed it was just WC3 being WC3.
 
Level 18
Joined
Jan 1, 2018
Messages
728
is there a connection between those definitions and the fact that WC3 doesn't seem to like using the WC3 types as indexes? For a while me and Damage had some dictionaries that mapped player objects to an actual class containing more info on that player, but then after a while it started throwing a hissy fit and crashing since it wasn't allowed (despite working fine prior). We kind of assumed it was just WC3 being WC3.
I never had issues using dictionaries with TKey being a wc3 type, and if it worked for you as well before, there's probably some other issue causing the crashing.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Quick update on the progres I made so far re-implementing War3Net.Build's MapBuilder: I decided that I will keep the old MapBuilder at least for the preview version(s) (hoping to release the first preview next week), though it has been renamed to LegacyMapBuilder since I also have a new MapBuilder class. The LegacyMapBuilder no longer supports all features it previously had, but it should still work in most cases. To give you an idea of how you can use the new MapBuilder (which the LegacyMapBuilder uses), here's how I re-implemented the Build method:
C#:
        public BuildResult Build(ScriptCompilerOptions compilerOptions, params string[] assetsDirectories)
        {
            if (compilerOptions is null)
            {
                throw new ArgumentNullException(nameof(compilerOptions));
            }

            var map = new Map(compilerOptions.MapInfo!, compilerOptions.MapEnvironment!)
            {
                Sounds = compilerOptions.MapSounds,
                PreviewIcons = compilerOptions.MapIcons,
                Regions = compilerOptions.MapRegions,
                AbilityObjectData = compilerOptions.MapAbilityData,
                BuffObjectData = compilerOptions.MapBuffData,
                DestructableObjectData = compilerOptions.MapDestructableData,
                DoodadObjectData = compilerOptions.MapDoodadData,
                ItemObjectData = compilerOptions.MapItemData,
                UnitObjectData = compilerOptions.MapUnitData,
                UpgradeObjectData = compilerOptions.MapUpgradeData,
                Doodads = compilerOptions.MapDoodads,
                Units = compilerOptions.MapUnits,
                CustomTextTriggers = compilerOptions.MapCustomTextTriggers,
                Triggers = compilerOptions.MapTriggers,
                TriggerStrings = compilerOptions.MapTriggerStrings,
                Cameras = compilerOptions.MapCameras,
                PathingMap = compilerOptions.MapPathingMap,
                ShadowMap = compilerOptions.MapShadowMap,
            };

            var builder = new MapBuilder(map);
            foreach (var assetDirectory in assetsDirectories)
            {
                builder.AddFiles(assetDirectory);
            }

            if (map.Info.ScriptLanguage == ScriptLanguage.Lua)
            {
                var csc = compilerOptions.Debug ? "-define:DEBUG" : null;
                var csproj = Directory.EnumerateFiles(compilerOptions.SourceDirectory, "*.csproj", SearchOption.TopDirectoryOnly).Single();
                var compiler = new Compiler(csproj, compilerOptions.OutputDirectory, string.Empty, null, csc, false, null, string.Empty)
                {
                    IsExportMetadata = false,
                    IsModule = false,
                    IsInlineSimpleProperty = false,
                    IsPreventDebugObject = true,
                    IsCommentsDisabled = compilerOptions.Optimize,
                    IsDecompilePackageLibs = compilerOptions.DecompilePackageLibs,
                };

                var compileResult = string.IsNullOrEmpty(compilerOptions.CommonJPath) || string.IsNullOrEmpty(compilerOptions.BlizzardJPath)
                    ? map.CompileScript(compiler, compilerOptions.Libraries)
                    : map.CompileScript(compiler, compilerOptions.Libraries, compilerOptions.CommonJPath, compilerOptions.BlizzardJPath);

                if (!compileResult.Success)
                {
                    return new BuildResult(false, compileResult, Array.Empty<Diagnostic>());
                }
            }
            else
            {
                map.CompileScript(); // For testing - compiles the map as JASS.
            }

            var archiveCreateOptions = new MpqArchiveCreateOptions
            {
                ListFileCreateMode = _generateListFile ? MpqFileCreateMode.Overwrite : MpqFileCreateMode.Prune,
                AttributesCreateMode = MpqFileCreateMode.Prune,
                BlockSize = _blockSize,
            };

            builder.Build(Path.Combine(compilerOptions.OutputDirectory, _outputMapName), archiveCreateOptions);

            return new BuildResult(true, null, Array.Empty<Diagnostic>());
        }

You may notice that the common.j and Blizzard.j files are now required to compile (if you don't pass them, it tries to find them in "/Documents/Warcraft III/JassHelper"). This is because instead of using an abstract class with way too many generic type arguments, I only implement the main/config function generator for Jass, then I use my JassToLuaTranspiler to convert these functions to lua. This transpiler requires common.j and Blizzard.j, which is why the MapBuilder now also requires them.
 
Level 18
Joined
Jan 1, 2018
Messages
728
War3Net.Build v5.0.0-preview2 is now available, please try it and let me know if there are any issues.
The fastest way to update is to simply rename MapBuilder to LegacyMapBuilder. There may be other breaking changes that cause an error, if you're not sure how to solve these, let me know. Some examples of breaking changes (and how to fix):
- MapXXX.Parse method no longer exists: Use the BinaryReader extension methods instead;
- Player.Create method no longer exists: you can simply use the constructor now;
- Player properties: some have been renamed (eg PlayerNumber -> Id), others have a different type (the boolean properties use a flags enum now, the startloc uses Vector2, the playerflags use BitMask);
- MapInfo no longer has a SetPlayers method, for this you can directly access the Players list now.
There's probably a lot more breaking changes (a lot of these are because I tried to make the War3Net.Build.Core API more consistent), these are just a few that I came across when updating my own map project.

Also note that War3Net.Build now targets .NET 5.0, so if your project still targets .NET Core 3.1, you must update this as well.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Updated to preview3, for convenience I added back the SetPlayers method for ForceData as an extension method. The update also includes new methods in MapScriptBuilder that help you create the C# API for generated global variables.
EDIT: preview4 fixes C# API generators so they have CSharpLua.Template, example:
C#:
var mapScriptBuilder = new MapScriptBuilder();
mapScriptBuilder.SetDefaultOptionsForMap(map);

var jassToCSharpTranspiler = new JassToCSharpTranspiler();
jassToCSharpTranspiler.ApplyCSharpLuaTemplateAttribute = true;
jassToCSharpTranspiler.JassToLuaTranspiler = new JassToLuaTranspiler();

var compilationUnit = SyntaxFactory.CompilationUnit(
    default,
    SyntaxFactory.SingletonList(
        SyntaxFactory.UsingDirective(
            SyntaxFactory.Token(SyntaxKind.StaticKeyword),
            null,
            SyntaxFactory.ParseName($"{nameof(War3Api)}.{nameof(War3Api.Common)}"))),
    default,
    new SyntaxList<MemberDeclarationSyntax>(
        SyntaxFactory.NamespaceDeclaration(
            SyntaxFactory.ParseName("GeneratedGlobals"),
            default,
            default,
            SyntaxFactory.List(new MemberDeclarationSyntax[]
            {
                SyntaxFactory.ClassDeclaration(
                    default,
                    new SyntaxTokenList(
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword),
                        SyntaxFactory.Token(SyntaxKind.StaticKeyword)),
                    SyntaxFactory.Identifier("Regions"),
                    null,
                    null,
                    default,
                    new SyntaxList<MemberDeclarationSyntax>(
                        mapScriptBuilder.RegionsApi(map, jassToCSharpTranspiler))),
                SyntaxFactory.ClassDeclaration(
                    default,
                    new SyntaxTokenList(
                        SyntaxFactory.Token(SyntaxKind.PublicKeyword),
                        SyntaxFactory.Token(SyntaxKind.StaticKeyword)),
                    SyntaxFactory.Identifier("Units"),
                    null,
                    null,
                    default,
                    new SyntaxList<MemberDeclarationSyntax>(
                        mapScriptBuilder.UnitsApi(map, jassToCSharpTranspiler))),
            }))));

compilationUnit.NormalizeWhitespace().WriteTo(streamWriter);
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Just released preview6, this version supports using ScriptCompilerOptions.DecompilePackages, which is a list of package names for which decompiled source code should be included in your map script.
The names are case-insensitive, and must be separated by a ;
In the future I may add support wildcards, for example "System.*", "War3Api.*", and "*.Sources" could be set as the default for packages that should NOT be decompiled, and all others should be.
 
Level 18
Joined
Jan 1, 2018
Messages
728
War3Net.Build has been updated to v5.0.0-rc1, this will probably be the last pre-release version.
If you haven't tried out the preview versions yet, now might be a good time to try out the latest version. Based on feedback, I made some additional changes to the library that should make it easier to deal with the breaking changes.

This version supports using wildcards for the package decompile feature. If you set ScriptCompilerOptions.DecompilePackageLibs to true, it will use the default blacklist to exclude certain packages from being decompiled, including any System packages.
To have more control over which packages are decompiled or not, you can instead use ScriptCompilerOptions.DecompilePackages (whitelist) and ScriptCompilerOptions.ExcludeDecompileLibs (blacklist).
If you're having issues with packages, the map script in war3map.lua also lists all packages that have been used during compilation, and which of those have been decompiled. This can be useful for debugging.

I also made some changes so that the ScriptCompilerOptions.LobbyMusic is once again supported.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Updated War3Net.Build to v5.0.0, since there are breaking changes from v1.7.0, you may want to keep using the old version for existing projects, but if you decide to update you can use this commit to help you migrate.
 
Level 5
Joined
Mar 8, 2012
Messages
44
With the addition of the decompile options and the whitelist/blacklist, is it possible to split packages? I was under the belief that it was, but I'm having difficulties actually doing so. I had some issues about the same directory having multiple projects earlier but seem to have cleared that up, but now it's having issues resolving War3Api.Common. I believe that it may not be using the blacklist when resolving package dependencies?

Code:
ICSharpCode.Decompiler.Metadata.AssemblyResolutionException
  HResult=0x80070002
  Message=Failed to resolve assembly: 'War3Api.Common, Version=1.32.0.0, Culture=neutral, PublicKeyToken=null'
  Source=ICSharpCode.Decompiler
  StackTrace:
   at ICSharpCode.Decompiler.Metadata.UniversalAssemblyResolver.ResolveInternal(IAssemblyReference name)
   at ICSharpCode.Decompiler.Metadata.UniversalAssemblyResolver.FindAssemblyFile(IAssemblyReference name)
   at ICSharpCode.Decompiler.Metadata.UniversalAssemblyResolver.Resolve(IAssemblyReference name)
   at ICSharpCode.Decompiler.TypeSystem.DecompilerTypeSystem..ctor(PEFile mainModule, IAssemblyResolver assemblyResolver, TypeSystemOptions typeSystemOptions)
   at ICSharpCode.Decompiler.TypeSystem.DecompilerTypeSystem..ctor(PEFile mainModule, IAssemblyResolver assemblyResolver)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.CreateTypeSystemFromFile(String fileName, DecompilerSettings settings)
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler..ctor(String fileName, DecompilerSettings settings)
   at CSharpLua.LuaSyntaxGeneratorFactory.<>c__DisplayClass7_1.<CreateGeneratorForProject>b__8(String lib)
   at System.Linq.Enumerable.SelectArrayIterator`2.ToArray()
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at CSharpLua.LuaSyntaxGeneratorFactory.CreateGeneratorForProject(CompilerSettings settings, SettingInfo settingInfo)
   at CSharpLua.LuaSyntaxGeneratorFactory.CreateGenerator(CompilerSettings settings, SettingInfo settingInfo)
   at CSharpLua.Compiler.GetGenerator()
   at CSharpLua.Compiler.CompileSingleFile(Stream target, IEnumerable`1 luaSystemLibs)
   at War3Net.Build.Extensions.MapExtensions.CompileScript(Map map, Compiler compiler, MapScriptBuilder mapScriptBuilder, IEnumerable`1 luaSystemLibs, String commonJPath, String blizzardJPath)
   at War3Net.Build.Extensions.MapExtensions.CompileScript(Map map, Compiler compiler, IEnumerable`1 luaSystemLibs, String commonJPath, String blizzardJPath)
 
Level 18
Joined
Jan 1, 2018
Messages
728
With the addition of the decompile options and the whitelist/blacklist, is it possible to split packages? I was under the belief that it was, but I'm having difficulties actually doing so. I had some issues about the same directory having multiple projects earlier but seem to have cleared that up, but now it's having issues resolving War3Api.Common. I believe that it may not be using the blacklist when resolving package dependencies?
Can you check if the .dll files exist at 'C:\Users\USER\.nuget\packages\war3api.common\1.32.9\lib'?
There shouldn't be any issues with the blacklist/whitelist, what strings are you currently using? And have you tried using the default (by setting DecompilePackageLibs to true)?

EDIT: NVM I see the problem already, it's trying to decompile War3Api.Blizzard but it can't find War3Api.Common, I'll try fixing it.
EDIT2: Hacky af fix because I need sleep right now:
- Download attached file and extract to local NuGet folder feed (if you don't have one yet, create one in visual studio > tools > options > nuget package manager > package sources > source = C:\blabla)
- Add to launcher project: <PackageReference Include="War3Net.CSharpLua" Version="1.7.9-test" />
- Run launcher. It will still give an erro but a different one.
- Go to 'C:\Users\USER\Documents\CSharpLua\packages\War3Api.Blizzard\1.32.9\lib\netstandard1.0', remove assembly attributes, add 'using static War3Api.Common;'.
- Run launcher again, it just works. (unless you have more packages than only war3api, basically you need to remove the assembly attributes because you can't have duplicates of those, and you need to manually add the using blabla because it currently cannot resolve the assemblies for its dependencies, I only disabled throwing an error when this happens).
 

Attachments

  • war3net.csharplua.1.7.9-test.zip
    222.4 KB · Views: 37
Last edited:
Level 5
Joined
Mar 8, 2012
Messages
44
Hm yeah I had actually created a test solution that integrates War3Net into WCSharp so that I can modify the build chain to experiment, and I had been stumbling around a bit with the ThrowOnAssemblyResolveErrors that I'm guessing you used for that version. After some pointers from your post I have indeed gotten decompiling two WCSharp packages (one of which depends on the other) to work. It's hacky for now, but I'm happy to see that it's working at all.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hm yeah I had actually created a test solution that integrates War3Net into WCSharp so that I can modify the build chain to experiment, and I had been stumbling around a bit with the ThrowOnAssemblyResolveErrors that I'm guessing you used for that version. After some pointers from your post I have indeed gotten decompiling two WCSharp packages (one of which depends on the other) to work. It's hacky for now, but I'm happy to see that it's working at all.
Yes I used the ThrowOnAssemblyResolveErrors setting, however now that I fixed the issues I set it back to its default value true.

I only updated War3Net.CSharpLua to v1.7.9, so because War3Net.Build still references v1.7.8, you need to explicitly add this packagereference to the launcher project. This version fixed two bugs: removed assembly attributes from decompilation output to prevent duplicate assembly attribute errors, and add search directories for packages to prevent assembly not found errors.

Note that the decompiler will only run if the output .cs file does not exist, so if it's currently in a broken state, you need to delete it first. If you manually fixed the .cs file already you don't need to do this.
 
Level 5
Joined
Mar 8, 2012
Messages
44
So I had some time available again and finished up most of the changes that I wanted to do for the next (batch of) WCSharp update.
Unfortunately I'm running into some difficulties with testing, as it's throwing exceptions due to projects that get referenced by multiple projects.
To be precise, this isn't for NuGet packages, I'm gonna assume for now that that works correctly. Right now I'm just trying to add a direct reference to the other projects so I can determine whether things are actually working correctly.

This problem isn't too hard to solve, since I can just copy/paste the .cs files into my source project and be done with it, but well, ideally it'd just handle these references correctly.
The exact exception is thrown by ProjectHelper.cs:45, and I've further confirmed that you get ambiguous reference errors when you just remove that exception.

EDIT: If you need a test case, Orden4/WCSharp should run out-of-the-box (use WCSharp.sln, may have to adjust exe path in App.config in Launcher).
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
So I had some time available again and finished up most of the changes that I wanted to do for the next (batch of) WCSharp update.
Unfortunately I'm running into some difficulties with testing, as it's throwing exceptions due to projects that get referenced by multiple projects.
Thanks for reporting this bug, it's fixed in War3Net.CSharpLua v1.7.10
 
Level 18
Joined
Jan 1, 2018
Messages
728
Thanks for the quick update, but it doesn't seem to be going correct still. It's getting past the multiple references now, but is instead throwing errors on supposed ambiguous references.
When using WCSharp.sln it all works for me (only get an exception that output folder doesn't exist when writing war3map.lua, but that's easily solved with 'Directory.CreateDirectory(OUTPUT_FOLDER_PATH);').
With WCSharpBuild.sln I'm getting a CSharpLua bug exception, because Contract.Assert fails somewhere. Nothing about ambiguous references though. Maybe you need to pull the latest changes in the CSharpLua submodule?
 
Level 5
Joined
Mar 8, 2012
Messages
44
When using WCSharp.sln it all works for me (only get an exception that output folder doesn't exist when writing war3map.lua, but that's easily solved with 'Directory.CreateDirectory(OUTPUT_FOLDER_PATH);').
With WCSharpBuild.sln I'm getting a CSharpLua bug exception, because Contract.Assert fails somewhere. Nothing about ambiguous references though. Maybe you need to pull the latest changes in the CSharpLua submodule?
Hmm, I'm still getting the exception, both when using Launcher with War3Net.CSharpLua 1.7.10 and LauncherBuild with master War3Net/CSharp.lua branches. I pushed some updates to the development branch again (including a fix for the directory issue you pointed out), afterwards deleted & cloned the repo anew to make sure there's nothing hidden that could cause issues. I even deleted the CSharpLua folder in my documents. But still, both solutions are getting ambiguous reference errors when compiling the map.

I tried to set up a minimum test case for this afterwards, separate from the WCSharp solution, but I'm not actually getting any ambiguous reference errors for that one... it may be some specific use case which triggers it, so I can keep searching for a bit... the alternative is that there is still somewhere an outdated file that is causing issues, but I don't know where that could be at this point.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hmm, I'm still getting the exception, both when using Launcher with War3Net.CSharpLua 1.7.10 and LauncherBuild with master War3Net/CSharp.lua branches. I pushed some updates to the development branch again (including a fix for the directory issue you pointed out), afterwards deleted & cloned the repo anew to make sure there's nothing hidden that could cause issues. I even deleted the CSharpLua folder in my documents. But still, both solutions are getting ambiguous reference errors when compiling the map.

I tried to set up a minimum test case for this afterwards, separate from the WCSharp solution, but I'm not actually getting any ambiguous reference errors for that one... it may be some specific use case which triggers it, so I can keep searching for a bit... the alternative is that there is still somewhere an outdated file that is causing issues, but I don't know where that could be at this point.
Apparently I did forget to push some commits, though that shouldn't make a difference if the sln that uses package references also doesn't work. Maybe clean+rebuild your solutions or something.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Alright I've solved the issue. The problem was that I had conditional references along the lines of:
XML:
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <ProjectReference Include="..\WCSharp.Events\WCSharp.Events.csproj" />
    <ProjectReference Include="..\WCSharp.Shared\WCSharp.Shared.csproj" />
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PackageReference Include="WCSharp.Events" Version="0.1.0" />
    <PackageReference Include="WCSharp.Shared" Version="0.1.0" />
  </ItemGroup>
While on a wild goose chase I figured out that I should add the -debug argument to the csc parameter, but even so it still grabbed both item groups (i.e. both the packages and projects). In practice though it only gave an error on Shared, as the Event v0.1.0 package doesn't exist yet, and I guess it just silently ignores that? This is also the reason why you didn't have an error, as I'm guessing you've never downloaded v0.1.0 of WCSharp.Shared.

Anyway, it seems that whatever is being used for fetching references doesn't evaluate the conditions, even if Configuration/Platform are configured. I've opted to just remove the conditional references. They had their downsides anyway and I'm not even sure if there was still a purpose to them.

EDIT: Actually I just remembered that it's mainly to easily switch between release-with-package-requirements and general debug testing. I guess I can just create a new set of projects for it that I just copy/paste into when I'm ready for a release? I dunno, if this is easily fixed then great I guess, but otherwise I'll just figure out a new way of easily handling release and testing.
 
Last edited:
Level 18
Joined
Jan 1, 2018
Messages
728
Alright I've solved the issue. The problem was that I had conditional references along the lines of:
XML:
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <ProjectReference Include="..\WCSharp.Events\WCSharp.Events.csproj" />
    <ProjectReference Include="..\WCSharp.Shared\WCSharp.Shared.csproj" />
  </ItemGroup>
  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <PackageReference Include="WCSharp.Events" Version="0.1.0" />
    <PackageReference Include="WCSharp.Shared" Version="0.1.0" />
  </ItemGroup>
While on a wild goose chase I figured out that I should add the -debug argument to the csc parameter, but even so it still grabbed both item groups (i.e. both the packages and projects). In practice though it only gave an error on Shared, as the Event v0.1.0 package doesn't exist yet, and I guess it just silently ignores that? This is also the reason why you didn't have an error, as I'm guessing you've never downloaded v0.1.0 of WCSharp.Shared.

Anyway, it seems that whatever is being used for fetching references doesn't evaluate the conditions, even if Configuration/Platform are configured. I've opted to just remove the conditional references. They had their downsides anyway and I'm not even sure if there was still a purpose to them.

EDIT: Actually I just remembered that it's mainly to easily switch between release-with-package-requirements and general debug testing. I guess I can just create a new set of projects for it that I just copy/paste into when I'm ready for a release? I dunno, if this is easily fixed then great I guess, but otherwise I'll just figure out a new way of easily handling release and testing.
I do the same thing in War3Net (debug projectreferences, release packagereferences), it's useful for debugging since packages are built in release mode, which makes them annoying to debug (due to optimizations).

Afaik the library I use to parse the .csproj files does not support evaluating the conditions, though I'm not using the latest version so I'll have to check if this feature has been implemented by now or if it's still missing.
 
Level 5
Joined
Mar 8, 2012
Messages
44
Hi again, just a friendly request for an update of the CSharp.lua fork. I'd like to move towards final testing and release of updated WCSharp components either in the weekend or early next week, and I need some of the latest transpiler fixes for the libraries to be functional.
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hi again, just a friendly request for an update of the CSharp.lua fork. I'd like to move towards final testing and release of updated WCSharp components either in the weekend or early next week, and I need some of the latest transpiler fixes for the libraries to be functional.
Updated to v1.7.11
 
Level 13
Joined
Jun 1, 2008
Messages
360
Hi Drake,
cool project! When I saw this, I immediately wanted to make a new map :)

Is it currently possible to load existing terrain, doodads etc.? The LegacyMapBuilder doesn't have that 3rd parameter anymore and MapEnvironment has no parse function or respective constructor to take an existing map.

Cheers
 
Level 18
Joined
Jan 1, 2018
Messages
728
Hi Drake,
cool project! When I saw this, I immediately wanted to make a new map :)

Is it currently possible to load existing terrain, doodads etc.? The LegacyMapBuilder doesn't have that 3rd parameter anymore and MapEnvironment has no parse function or respective constructor to take an existing map.

Cheers
The old MapBuilder never had a third parameter, it used 'params' which allows you to pass individual arguments and automatically convert them to an array. The LegacyMapBuilder works the same, however I checked the code and it seems that I forgot to support the path being an mpq archive file (aka a map, .w3m/.w3x file), it now only supports folders.
The easiest way to fix this would probably be to use the new MapBuilder instead of the LegacyMapBuilder. Then you can use the AddFiles method to add the files from an mpq archive. Note that this method has multiple overloads, and the one that takes a string path has the same 'issue' as the LegacyMapBuilder that it only supports a folder, not an mpq archive. So you need to use the overload that takes an MpqArchive, which you can simply obtain with 'using var mpqArchive = MpqArchive.Open(path);'

MapEnvironment.Parse being gone is one of the breaking changes from v5, all war3map files can now be loaded using the extension methods for BinaryReader and StreamReader, which are found in the namespace 'War3Net.Build.Extensions'.
 
Level 13
Joined
Jun 1, 2008
Messages
360

So now I have this:
C#:
            var map = new Map(compilerOptions.MapInfo!, compilerOptions.MapEnvironment!)
            {
                Sounds = compilerOptions.MapSounds,
                PreviewIcons = compilerOptions.MapIcons,
                Regions = compilerOptions.MapRegions,
                AbilityObjectData = compilerOptions.MapAbilityData,
                BuffObjectData = compilerOptions.MapBuffData,
                DestructableObjectData = compilerOptions.MapDestructableData,
                DoodadObjectData = compilerOptions.MapDoodadData,
                ItemObjectData = compilerOptions.MapItemData,
                UnitObjectData = compilerOptions.MapUnitData,
                UpgradeObjectData = compilerOptions.MapUpgradeData,
                Doodads = compilerOptions.MapDoodads,
                Units = compilerOptions.MapUnits,
                CustomTextTriggers = compilerOptions.MapCustomTextTriggers,
                Triggers = compilerOptions.MapTriggers,
                TriggerStrings = compilerOptions.MapTriggerStrings,
                Cameras = compilerOptions.MapCameras,
                PathingMap = compilerOptions.MapPathingMap,
                ShadowMap = compilerOptions.MapShadowMap,
            };
            var mapBuilder = new MapBuilder(map);
            using var mpqArchive = MpqArchive.Open(BaseMapPath);
            mapBuilder.AddFiles(mpqArchive);
            mapBuilder.AddFiles(AssetsFolderPath, "*", SearchOption.AllDirectories);
            mapBuilder.Build(absoluteMapPath);
I took some lines from Drake53/War3Net
In the minimap preview it looks alright (I see terrain and starting locations), but I can't start the map; forces and players are missing.
I guess I'd need a script builder as well, but the Libraries variable is internal, so I can't use it.
Or am I completely on the wrong track?
 
Level 18
Joined
Jan 1, 2018
Messages
728
So now I have this:
C#:
            var map = new Map(compilerOptions.MapInfo!, compilerOptions.MapEnvironment!)
            {
                Sounds = compilerOptions.MapSounds,
                PreviewIcons = compilerOptions.MapIcons,
                Regions = compilerOptions.MapRegions,
                AbilityObjectData = compilerOptions.MapAbilityData,
                BuffObjectData = compilerOptions.MapBuffData,
                DestructableObjectData = compilerOptions.MapDestructableData,
                DoodadObjectData = compilerOptions.MapDoodadData,
                ItemObjectData = compilerOptions.MapItemData,
                UnitObjectData = compilerOptions.MapUnitData,
                UpgradeObjectData = compilerOptions.MapUpgradeData,
                Doodads = compilerOptions.MapDoodads,
                Units = compilerOptions.MapUnits,
                CustomTextTriggers = compilerOptions.MapCustomTextTriggers,
                Triggers = compilerOptions.MapTriggers,
                TriggerStrings = compilerOptions.MapTriggerStrings,
                Cameras = compilerOptions.MapCameras,
                PathingMap = compilerOptions.MapPathingMap,
                ShadowMap = compilerOptions.MapShadowMap,
            };
            var mapBuilder = new MapBuilder(map);
            using var mpqArchive = MpqArchive.Open(BaseMapPath);
            mapBuilder.AddFiles(mpqArchive);
            mapBuilder.AddFiles(AssetsFolderPath, "*", SearchOption.AllDirectories);
            mapBuilder.Build(absoluteMapPath);
I took some lines from Drake53/War3Net
In the minimap preview it looks alright (I see terrain and starting locations), but I can't start the map; forces and players are missing.
I guess I'd need a script builder as well, but the Libraries variable is internal, so I can't use it.
Or am I completely on the wrong track?
The ScriptCompilerOptions class is obsolete, you should only use it if you're using the LegacyMapBuilder.
You can use Map.Open instead of the constructor, then you also won't have to use MpqArchive.Open.
Libraries is set in the constructor of ScriptCompilerOptions (usually CSharpLua.CoreSystem.CoreSystemProvider.GetCoreSystemFiles()).
 
Top