armory3d / armory

3D Engine with Blender Integration
https://armory3d.org
zlib License
3.05k stars 316 forks source link

Add Modding Support #748

Closed zicklag closed 4 years ago

zicklag commented 6 years ago

I know this is kind of a large topic, but it would be awesome if armory could support a modding workflow that would allow you to create blends and build them as a mod that could be loaded into an Armory game.

I'm assuming that assets could very easily be packed, just like they already are, and then they could be loaded dynamically into a game, but I don't know how you would handle mod code. You would probably have to be able to write Haxe code and build it to native libraries ( like .so, .dll ), or use an extension language ( probably better, or at least easier ) like Lua. Lua could probably be integrated similar to how Bullet physics were integrated, with a Haxe library wrapping both a native C and and a ported Javascript library.

Another idea for the extension language could be to bundle V8 for evaluating Javascript on the non web/krom targets. That could be cool too. :slightly_smiling_face:

EDIT: Web assembly is yet another option I forgot about. Because Javascript and web assembly are already supported on the web/krom platforms, it might be better to try to support it on the native targets than it would be to try to support something like Lua on the web targets.

zicklag commented 6 years ago

@luboslenco I think I have a really good idea for how to handle modding ( from a coding perspective ), but I am not 100% sure it is possible and though I am trying to implement it myself, I need some help/advice if you get a chance.

My plan is to allow you to write mods in Haxe, but compile the mod to Javascript for the Krom/Browser targets and compile the mod to Lua for the C++ targets. In addition, I want to expose the entire ( or very nearly the entire ) Kha and Iron API's to Javascript and Lua through Haxe macros. This would make writing mods nearly identical to writing normal game code, except that it could be loaded at runtime.

I already successfully made a Haxe initialization macro that would add the :expose metadata to all of the Iron and Kha APIs automatically. This works for exposing the APIs on the Javascript targets similar to how you did with your call_hx example except it exposes the whole Iron API instead of just a single class. I planned on doing something similar with the C++ targets where I would use a macro to add whatever code necessary to expose the APIs though Lua bindings ( I don't know much about what it takes to do that yet ).

Assuming I can get Lua bindings to work like I was able to get the Javascript bindings to, the next step is being able to write the mod in Haxe instead of Lua or Javascript. In order to do that I need Haxe externs for Kha and Iron. This is where I am having trouble. I'm looking for a way to automatically make Haxe externs out of the Iron and Kha libraries. I tried to use an initialization macro to mark all of the classes in the packages as extern, but you run into issues because the methods still have method bodies and are still evaluated by the compiler. I also tried to use a type building macro to null out the method bodies, but it still wasn't working and I'm not sure exactly why.

If you have any guidance, thoughts, or ideas they would be appreciated.

zicklag commented 6 years ago

OK so after doing a lot of experimentation trying to turn the Iron and Kha libraries into externs during the mod compilation, I decided I am going to generate physical Haxe extern files during compilation of the game. The new plan is:

  1. Use a Haxe initialization macro to expose the game libraries ( Iron, Kha, and others ) to Javascript on Javascript builds ( working ) and generate Lua bindings for c++ builds ( not tested yet ) during compilation of the main game.
  2. Use a Haxe initialization macro to generate Haxe files in a separate build folder for the Haxe externs for the game libraires ( starting work on this now ).
  3. Allow you to write Haxe mods that can include the generated externs and compile mods to Javascript for web/krom targets and Lua for C++ targets.
zicklag commented 6 years ago

So after spending hours trying to figure it out I finally found the haxe.macro.Compiler.exclude macro that I modified to a keepOnly macro that allows you to write a mod in Haxe, compile it to Javascript, and load it into Krom/Web games. I have an example project in the attached zip: modding-example.zip

Also, after looking into Lua a little bi,t my unprofessional evaluation ( :stuck_out_tongue_winking_eye: ) is that it might be impractical to try and automatically bind the entire Kha/Iron APIs to Lua. Additionally I realized that most of the modding implementation should be in Kha not Armory so I'm going to open up an issue in Kha to discuss that there.

zicklag commented 6 years ago

After opening an issue in Kha, I found out from @RobDangerous that Krom will be coming to consoles soon and has actually performed better than the C++ target in benchmarks run by @luboslenco. This means that I don't really need to get a modding setup for the C++ target and I can just use Krom.

Now I'm going to attempt to build a modding workflow into Armory for Krom.

HeadClot commented 6 years ago

Let me know how this goes. :)

It also may be worth looking into Mod.io for hosting mod files. They have a Haxe Wrapper that is compatible with Kha.

zicklag commented 6 years ago

Hey @HeadClot glad that someone else is interested in modding. So, it is going really well. In fact, today I just got the first Armory mod ever working! I have it so Armory can load a scene at runtime from a mod including it's shaders, meshes, and textures. Code isn't loading dynamically yet, but I've already tested that in a different example, so I will have that working soon. I know it doesn't mean much but here is a screen-shot of the First Armory3D Mod Ever!

first ever armory3d mod

I'm going to be submitting PR's for both Armory and Iron as soon as I have a base working example.

Turupawn commented 6 years ago

@zicklag congratulations! It's good to see mod integration on engine level, especially on 3d game engines. I'm working on mod.io and got here because @HeadClot (thanks!) mentioned this thread to us. Our aim is to make modding simpler to game devs. As @HeadClot said, we have a wrapper compatible with Haxe. Aground, a survival crafting game, just launched using this Haxe wrapper. We offer hosting space and automatic mod installs so it would be cool to combine it with what you are doing. Keep it up! And feel free to come and chat with us at our discord.

zicklag commented 6 years ago

@Turupawn Thanks for the heads up! If I get the time I will definitely look into mod.io. Sounds cool.

zicklag commented 6 years ago

My work so far can be found at armory3d/iron#43 and armory3d/armory#819.

zicklag commented 6 years ago

Yay! I just got mods working with dynamically loadable code and assets! I'm going to create an example very soon now and maybe polish a bit of the UX a little bit, but it's working as far as I can tell. I've updated armory3d/armory#819 with the latest code.

kenthinson commented 6 years ago

@zicklag congratulations! I'm sure some developers will be very happy to have this!

zicklag commented 5 years ago

For anybody who's interested, I've been busy for the last little bit and there have already been a lot of updates to Armory since I last worked on this! I'm going to have to do a little re-work around just a few minor changes that have been made recently, but hopefully I'll be spending some more time on this soon. I can't wait to get this functionality finished and merged in. :smile:

zicklag commented 5 years ago

I've nearly gotten modding ready, again. :smiley: This time I've got to get it merged. I've created a post outlining how it works on the Armory forums. I've just got one more issue ( I think ) that I still have to solve. If anybody wants to help me with that, it would be appreciated.

masterneme commented 5 years ago

Hi,

I don't know how are you designing this so I'll just comment that for modding I always loved how it worked in the Quake series.

You place everything in a separate folder and then you initialize it like this: quake2.exe +game name-of-the-folder and it just works.

Quake 3 had a Mods menu inside the game and you could switch mods right away.

And Unreal works in a similar fashion but you can actually browse the folder in-game and activate whatever you want.

Good luck, modding is an awesome feature.

zicklag commented 5 years ago

Hi @masterneme you will definitely be able to set up modding similar to how you described. I'm glad to see another person who thinks modding is awesome. Most of the system seems to be working pretty well right now. I'm doing some practical testing with it because the game I'm trying to write right now depends on it.

The cool part about the setup is that it doesn't put much restrictions on how your game uses it. It simply gives you the ability to load code and assets that weren't a part of the original game. Mods are developed inside of Blender exactly like the rest of your Armory game. Your game can provide a UI to let you select and enable/disable mods however it wishes.

I think that you can achieve one of the most streamlined and powerful modding workflows ever with it. If you have a FPS for example. Each weapon could be loaded from a separate mod with its modeling, surfacing, and code all stored in the blend. The code can be logic nodes or Haxe traits, and the logic for the gun's bullet path and firing mechanics can be totally unique to the mod and provide functionality that was never in the game before. You're not limited to changing pre-defined parameters, you can actually add your own logic to the weapon and interact with any game APIs so that it can communicate with other components of the game or other mods.

If a player wanted to make their own weapon, they would only need Blender, and the ArmorySDK. They could copy a weapon they like, double click the blend, change some modeling here, paint some textures there, mess with the rate-of-fire, press F5, go to their game, select the mod, and start using their weapon right away. If you code the game right, it should be able to load mods in the middle of game-play as well ( or a pause menu or something like that ).

AWESOME! :tada: :sparkles:

masterneme commented 5 years ago

That sounds excelent.

I read from the guys from Godot, when someone suggested adding modding features, that they're not going to do anything about that because it has the potential risk of someone introducing malicious code and being harmful to the computer when activating said mod.

What do you think about that?

zicklag commented 5 years ago

It's hilarious that you bring that up because I was the one writing code for the modding system in Godot before they decided that it wasn't worth it. :smiley:

As far as the security of executing mods, in short, even if we can't prevent all possibility for the execution of malicious code, the modding ability is worth it even if you only ever used your own mods and the risk is something you have to take into account, just like any other software that you install on your computer.

Still, I think it is good if we can do whatever makes sense to prevent unnecessary risk when executing mods. Currently the modding setup only works for Krom, which is a Javascript based runtime for Armory, and we might be able to limit the ability to execute arbitrary programs on your system in Mods through Krom ( I would have to ask Robert about that ). Other options for increased security is running the game in a OS specific sandbox such as Flatpack or Firejail for Linux or ksm ( maybe ). We'll have to look into it and probably discuss it with Lubos and Robert when it becomes more complete and more of a priority.

Again, even if we couldn't stop the malicious code, I think that it is worth giving the users the ability to hack the game. And it isn't like it will effect games that the authors don't want to be moddable.

masterneme commented 5 years ago

Oh look at that, what a fricking coincidence :laughing:

I agree with you, Unreal, Quake & Doom allow for almost everything to be inserted and they use C/C++, even if you take care I don't think that would stop a really skilled and motivated hacker to do something malicious.

I don't know how the mentioned games or for example Half-Life manage to do this but it has been working for 20+ years so they would be something to consider as a good research source for modding development.

CarlLee commented 5 years ago

War3 has a significant security hole in it's map engine until it's fixed in 1.24. It allows remote execution of arbituary code. However, the only reason so many people played and are still playing War3 is because of its custom maps. So I would say security risk is a small concern in regard of the significant attraction modding brings.

On another topic, I've read through the whole codebase in regard to the support of WebAssembly and Javascript. It seems to me that Armory3D is designed to map C++(Kore project) to Javascript/Haxe(Krom) by use of Chakra engine. Then it maps those exposed C++ functions in Javascript to WebAssembly. So any engine calls made in WebAssembly code would go through from WebAssembly interpreter to js engine and then to C++. Will this cause bottleneck? Is there any benchmark done for this?

zicklag commented 5 years ago

There isn't any benchmarking that I know of. The setup doesn't use WASM though because mods are written in Haxe, which compiles to JavaScript. It still has to make the JavaScript -> C++ jump like the rest of Armory does, but it isn't any worse than the rest of the Armory core running in JavaScript.

It does remove the ability to use Armory's native C builds, though, because it takes out the JavaScript layer and leaves no good way that I know of to dynamically load code in a similar manner.

I definitely agree about modding being more valuable than security.

CarlLee commented 5 years ago

There isn't any benchmarking that I know of. The setup doesn't use WASM though because mods are written in Haxe, which compiles to JavaScript. It still has to make the JavaScript -> C++ jump like the rest of Armory does, but it isn't any worse than the rest of the Armory core running in JavaScript.

It does remove the ability to use Armory's native C builds, though, because it takes out the JavaScript layer and leaves no good way that I know of to dynamically load code in a similar manner.

I definitely agree about modding being more valuable than security.

I really like the decision to support WebAssembly because it unlocks so much more potential for dynamic scripting than just one/two languages. Is there any way to bind native functions directly to WebAssembly VM? That would make the architecture even more awesome, and probably more efficient.

zicklag commented 5 years ago

I don't know of any "automatic" way for WebAssembly to bind to native functions. I think you would have to compile Krom with custom JavaScript/WASM bindings to do that.


There is some introduction to how WebAssembly can be used in armory here, but I think you have to manually specify the bindings between WebAssembly and armory, which make it fairly inconvenient to do support writing most of your game with it.


Also, I'm not sure if you misunderstood me, but modding won't use WebAssembly.

luboslenco commented 4 years ago

Link https://github.com/armory3d/armory/pull/969#issuecomment-626546474.