Oxidsys (WIP): A module system from the ground up.

正在查看此主题的用户

Oxidsys

As a newcomer to modding M&B, I've enjoyed the experience. I didn't completely take kindly to the module system however. While I greatly appreciate the M&B devs and their work, I found the module system to be terribly concentrated (50k lines of code in a single file!?), not particularly readable in most places, unpleasant to edit in (python lists/tuples is not a very fluid medium) and other various issues I'm not the first to come across. Overall I just found too many frustrations to ignore them. I wanted an environment that wouldn't make me painfully search all the time, make VSCode use over a gigabyte of RAM to edit the game scripts, have compile times that while not abysmal are certainly not fast and so on. That brings us to this project called Oxidsys.

Oxidsys is a work in progress completely redone module system with some distinguishing characteristics/goals.

No Python
Oxidsys does not rely on Python. You can have Python completely absent from your system and still define game content and compile it. This is because Oxidsys opts for a (in my own opinion) better more readable format for defining game content, and because Oxidsys itself is written Rust, therefore Python has been replaced for the compilation procedure.

JSON
I found the method with Python lists/tuples bad for readability and organization. Oxidsys instead makes use of JSON files in respective directories. For example, to add the Trade skill to the game, one defines skills/trade.json and gives it the following content (assuming identical to Vanilla).

插入代码块:
{
  "id" : "trade",
  "name" : "Trade",
  "flags" : ["base_att_4", "effects_party"],
  "max_level" : 10,
  "description" : "Every level of this skill reduces your trade penalty by 5%%. (Party Skill)"
}

One can quickly make sense of the data. Also, skills instead of being defined all in one file, they are defined file by file in the skills directory. This a philosophy Oxidsys holds in general to avoid giant messy globs of code/data, improving readability, organization, and modularity.

Oxid
Of course, not all content in a mod is simple data. Oxidsys provides a new grammar Oxid for scripting. The goal of Oxid is to offer some nice features for verbosity and safety, but for the most part to feel familiar to the original module system.

An example of how Oxid looks in comparison.

Original Script in the native module system:
插入代码块:
  ("game_get_skill_modifier_for_troop",
   [(store_script_param, ":troop_no", 1),
    (store_script_param, ":skill_no", 2),
    (assign, ":modifier_value", 0),
    (try_begin),
      (eq, ":skill_no", "skl_wound_treatment"),
      (call_script, "script_get_troop_item_amount", ":troop_no", "itm_book_wound_treatment_reference"),
      (gt, reg0, 0),
      (val_add, ":modifier_value", 1),
    (else_try),
      (eq, ":skill_no", "skl_trainer"),
      (call_script, "script_get_troop_item_amount", ":troop_no", "itm_book_training_reference"),
      (gt, reg0, 0),
      (val_add, ":modifier_value", 1),
    (else_try),
      (eq, ":skill_no", "skl_surgery"),
      (call_script, "script_get_troop_item_amount", ":troop_no", "itm_book_surgery_reference"),
      (gt, reg0, 0),
      (val_add, ":modifier_value", 1),
    (try_end),
    (set_trigger_result, ":modifier_value"),
    ]),

Oxid grammar:
插入代码块:
param troop_no;
param skill_no;

register r0;

local modifier_value;
assign modifier_value 0;
try_begin;
    eq skill_no skl::wound_treatment;
    call_script script::get_troop_item_amount troop_no itm::book_wound_treatment_reference;
    gt reg0 0;
    val_add modifier_value 1;
else_try;
    eq skill_no skl::trainer;
    call_script script::get_troop_item_amount troop_no itm::book_training_reference;
    gt reg0 0;
    val_add modifier_value 1;
else_try;
    eq skill_no skl::surgery;
    call_script script::get_troop_item_amount troop_no itm::book_surgery_reference;
    gt reg0 0;
    val_add modifier_value 1;
try_end;

set_trigger_result modifier_value;

This aspect of Oxidsys is still in a pretty early stage, but the parser is in fact mostly complete, and I am near being able to output a simple operation call list and compile it down to the .txt format. Nonetheless there is much work that will need to be done here.

Fast Build Times
There have already been projects that improve the build times, but Oxidsys has the potential to be the fastest. Being written in Rust, and purposefully avoiding IO bottlenecks by mostly working with content in memory before writing among other things, Oxidsys has the making for blazing fast build times. With the currently existing compilers, build speed is near instant. This is silly to brag about currently however without an actual port of Native yet.

Helpful Services
Oxidsys aims to offer various helpful utilities. A currently existing example is quick documentation access based currently from Lav's work. He is currently credited here
https://github.com/AustinHaugerud/oxidsys/blob/master/src/language/operations/mod.rs
but if Lav or someone who feels they can reasonably speak for Lav can comment on a better way to present this please let me know.

插入代码块:
C:\Users\harbinger\ClionProjects\oxidsys>cargo run -- documentation call_script
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target\debug\oxidsys.exe documentation call_script`
Calls specified script with or without parameters.
<script_id>:
[<script_param>...]:

TLDR
Oxidsys is my attempt at creating a tool that allows scripters to be more productive, write safer code, offer better modularity making tools ModMerger redundant, and just make modding better in general.

I would've been content keeping this project to myself until I felt it was mostly done, as I can keep writing Oxidsys itself on my own. I don't want this to be a tool that's just useful to me however. I want Oxidsys to be a robust widely useful tool to modders in this community as a whole. This is why I want to reach out to the community and ask for modders to give their input and feedback on what the kind of things they like and dislike, what's the biggest thing on their wish list were they to use a new module system, etc.

You can contact me here, on Discord (GoblinJenkins), or make an issue on my github repo linked below.

https://github.com/AustinHaugerud/oxidsys <- Come here to make issues and watch my progress.
 
If you can get it to compile a fully game-safe bytecode replacement for MS that won't cause mysterious crashes because it'll preserve strict ordering of things where we need that (essentially, whole swaths of TW's bytecode is for all intents and purposes fully hard-coded)... I'll try it out on something small, if I ever get Blood and Steel's next major iteration done. 

Given where it sounds like you're at, that'll be a while anyhow. 

I'd love to see something like this actually get Done, though, preferably before Bannerlord ships into what looks likely to be a very long Really Actually Alpha state.

I've been attacking these issues from the back end this go-around; I've been deconstructing TW's over-engineered verbosity, documenting all of the hard-coded Gotchas, etc., so that when the mod's out (I always release the MS source) people can maybe take it and run somewhere interesting, at least with SP mods.

To put it another way, I'm at 16K lines of code in module_scripts (that's one third of the original size!!!), other areas have shrunk by similar volumes... and I expect that to keep shrinking as I find more opportunities to prune it down.  I'm not at the final Grep Parser Script Kill of Dewm just yet, but I'm guessing I hit under 10K and have a game that, to players, is almost the same as Vanilla in terms of practical content and features.  It's kind of mind-blowing how much code is in this thing that replicates functionality elsewhere.

But don't be fooled into thinking it's enough to build something that can output valid bytecode.  The minimum acceptable "hello world" for something like this is something that, out of the box, compiles a compliant single-player start, Tutorial, Quick Battle, MP landing, mandatory Dialogs, etc., etc., etc.- there's a heck of a lot of work to do before you get to write your first "real" feature.  Plus documentation, which needs to be quite copious in the areas where it's going to be alien to MS veterans.

The biggest "gotcha" I think you'll find, besides the hard-coded stuff, is that the headers we aren't supposed to modify... get modified.  Sometimes heavily.  Some of them are merely collections of mutable data that people modify to get from A-->B, such as header_items, where every time I update the MS core from TW, I have to carefully port over my modified stuff (fun!).  And of course, big projects like mine also use bytecode tricks in the process_*.py, too, and other fun Stupid Tuple Tricks.
 
But don't be fooled into thinking it's enough to build something that can output valid bytecode.  The minimum acceptable "hello world" for something like this is something that, out of the box, compiles a compliant single-player start, Tutorial, Quick Battle, MP landing, mandatory Dialogs, etc., etc., etc.- there's a heck of a lot of work to do before you get to write your first "real" feature.  Plus documentation, which needs to be quite copious in the areas where it's going to be alien to MS veterans.

Of course, the idea is that when this "releases" there will be a port of the native mod to Oxidsys that builds the same game from a players perspective. Oxidsys might even come with inbuilt support for "templates", where the user could specify a native or barebones template perhaps.
oxidsys new --template <native/bare>
I haven't settled on this aspect yet though.

The documentation is important to, I haven't expressed here well what the differences Oxid introduces are, but that's largely because it'd be premature to talk about Oxid in detail too much at this point. Basically I've written a parser but there are other stages that need done. A basic implementation that'll at least allow you to port the native scripts however isn't too far off. The catch is I want Oxid to offer things like checking operation calls for example, which requires me to simply write out details about every single operation so that the compiler can see "Is that a correct number of arguments, are any of them nonsensical". There are fancy ideas like a pseudo-type state machine where I could strongly type data so that a user can't arbitrarily specify an agent id when one is expected. It depends on where I set the bar, and some bits will simply be a lot of grind to implement. (There are a little over 1000 operations from what I can tell).

The biggest "gotcha" I think you'll find, besides the hard-coded stuff, is that the headers we aren't supposed to modify... get modified.  Sometimes heavily.  Some of them are merely collections of mutable data that people modify to get from A-->B, such as header_items, where every time I update the MS core from TW, I have to carefully port over my modified stuff (fun!).  And of course, big projects like mine also use bytecode tricks in the process_*.py, too, and other fun Stupid Tuple Tricks.

This is one bit I need to look into more admittedly. I've seen several mods now that modify the header and process files. Oxidsys being written in Rust can't just be tweaked and run differently, the binary you build is what you get. I need to determine if it's feasible for Oxidsys to make these changes modders make unnecessary or if there is some level of extra control I need to try to expose to users. If you could detail these "bytecode/tuple" tricks further it can help me with this. Thanks  :grin: .

if I ever get Blood and Steel's next major iteration done.

Please do, I'm a big fan of the first iteration!
 
First off, thank you, I'm glad you liked my collection of kludges duct-taped together roughly into a mod :lol:  I am really actually trying, this time, to get to a much more... elegant space, where all of the code's actually purpose-built, rather than jerry-rigged stuff that barely worked and was sometimes dreadfully crashy on potato rigs :oops:

If you could detail these "bytecode/tuple" tricks further it can help me with this. Thanks  :grin: .
One example, from Blood and Steel's module_scripts, before we let the bytecode compiler do anything else:

插入代码块:
from header_common import *
from header_operations import *
from module_constants import *
from module_constants import *
from header_parties import *
from header_skills import *
from header_mission_templates import *
from module_items import *
from header_items import *
from module_troops import *
from header_troops import *
from header_triggers import *
from header_terrain_types import *
from header_music import *
from ID_animations import *

######Item damage / damage-type stripper, allowing Module System scripts to get these values back out of the item table with correct values. 
## original code by cmpxchg8b, with a ton of modification by xenoargh.
######This section's commented out but serves as an example
######REQUIRES header_items include in any context where you wish to use it
#def get_damage_amount(packed_value):
#	return packed_value & ibf_armor_mask;

#def get_damage_type(packed_value):
#	return packed_value >> iwf_damage_type_bits;

#packed_value = get_swing_damage(item[6])
#damage_amount = get_damage_amount(packed_value)
#damage_type = get_damage_type(packed_value) 

def get_damage_unpacked(packed_value):
 return packed_value & ibf_armor_mask;  

def set_item_flags():
  item_flags = []
  for i_item in xrange(len(items)):
   item_flags.append((item_set_slot, i_item, slot_item_difficulty, get_difficulty(items[i_item][6])))
   swing_damage = int(get_swing_damage(items[i_item][6]))
   swing_damage = int(get_damage_unpacked(swing_damage))
   thrust_damage = int(get_thrust_damage(items[i_item][6]))
   thrust_damage = int(get_damage_unpacked(thrust_damage))
   #swing_damage = swing_damage / 2
   #thrust_damage = thrust_damage / 2
   #if (swing_damage > 150):
    #swing_damage = 50
   #if (thrust_damage > 150):
    #thrust_damage = 50	
   #print str(items[i_item][0]) + " swing_damage: " + str(swing_damage) + " thrust_damage: " + str(thrust_damage)
   if (swing_damage >= thrust_damage):
    item_flags.append((item_set_slot, i_item, slot_item_max_damage, swing_damage))
   else:
    item_flags.append((item_set_slot, i_item, slot_item_max_damage, thrust_damage))
   if (len(items[i_item]) > 10):
    item_flags.append((item_set_slot, i_item, slot_item_female_only, items[i_item][10]))
   else:
    item_flags.append((item_set_slot, i_item, slot_item_female_only, 0))
   if (len(items[i_item]) > 11):
    item_flags.append((item_set_slot, i_item, slot_item_poisoned, items[i_item][11]))
   else:
    item_flags.append((item_set_slot, i_item, slot_item_poisoned, -1))
   if (len(items[i_item]) > 12):
    item_flags.append((item_set_slot, i_item, slot_item_ancient, items[i_item][12]))
   else:
    item_flags.append((item_set_slot, i_item, slot_item_ancient, -1))
   if (len(items[i_item]) > 13):
    item_flags.append((item_set_slot, i_item, slot_item_damage_bonus, items[i_item][13]))
   else:
    item_flags.append((item_set_slot, i_item, slot_item_damage_bonus, 0))	
  return item_flags[:]
def set_troop_flags():
 troop_flags = []
 for i_troop in xrange(len(troops)):
  troop_len = len(troops[i_troop])
  #print troop_len
  if (troop_len == 21):
   ##The following line is here to test that values are being transmitted correctly.
   #print "found exceptional troop:  " + str(troops[i_troop][0]) + " missile cap: " + str(troops[i_troop][16])    
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_missile_nonhero, troops[i_troop][16]))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_missile_hero, troops[i_troop][17]))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_nonhero, troops[i_troop][18]))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_hero, troops[i_troop][19]))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_speed_mult, troops[i_troop][20]))
  else:
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_missile_nonhero, 300))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_missile_hero, 300))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_nonhero, 300))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_damage_hero, 300))
   troop_flags.append((troop_set_slot, i_troop, slot_troop_speed_mult, 100))  
   #print "found normal troop:  " + str(troops[i_troop][0])
 return troop_flags[:]

What this does, is sets up a system where we can iterate over all the items and initialize all of them with Item Slot values:

插入代码块:
  
("game_start",
    [
	#Autoloot and special items properties script
	# initialization hook
	(call_script, "script_init_item_difficulties"),
	(call_script, "script_init_troop_difficulties"),

...and then we take advantage of the Module System being Python here, and run this while compiling.
插入代码块:
("init_item_difficulties", set_item_flags()),
("init_troop_difficulties", set_troop_flags()),

So, basically, the code does a giant injection here, calling thousands of operations.  Now, this does create ugly code-bloat, to be sure, eating precious RAM (although, I just wanna say that I have Blood and Steel operating pretty consistently around 500MB atm, and I expect that to go smaller as I focus down), so this isn't a parlor trick we want to use for trivial things.  But yeah, having a language-that's-actually-a-language matters for pulling things like this off.

Now... a few things about the headers.  I'm afraid that some of them really must be exposed.  The good news, not all of them; it's fairly safe to not expose your internal version of header_operations, for example, because that's just a list of integers the engine uses for internal calls.  But... one example:

插入代码块:
###################################################
# header_troops.py
# This file contains declarations for troops
# DO NOT EDIT THIS FILE!  UNLESS YOU'RE REALLY QUITE SURE YOU KNOW WHAT YOU'RE DOING.
###################################################

#Troop flags
tf_male           = 0
tf_female         = 1
tf_skeleton         = 2
tf_cannon_walker    = 3
tf_death_knight    = 4
tf_remnant    = 5
tf_flesh_golem    = 6
tf_siren   = 7

...and there's further non-stock modification in further sections, allowing me to set, say, STR to higher values than Vanilla allowed (because, well, Blood and Steel).

The biggest area of modification of headers that I've seen, generally (not trying to speak for Lav, Somebody, cmpxchg8b, etc., etc., who all generally got deeper down these rabbit holes than I dared, lol) is header_items; this is where there's a bunch of data is that we can mess with:

插入代码块:
itc_parry_onehanded = itcf_force_64_bits | itcf_parry_forward_onehanded| itcf_parry_up_onehanded | itcf_parry_right_onehanded |itcf_parry_left_onehanded
itc_parry_two_handed = itcf_force_64_bits | itcf_parry_forward_twohanded | itcf_parry_up_twohanded | itcf_parry_right_twohanded | itcf_parry_left_twohanded
itc_parry_polearm = itcf_parry_forward_polearm | itcf_parry_up_polearm | itcf_parry_right_polearm | itcf_parry_left_polearm

itc_cleaver = itcf_force_64_bits | itcf_slashright_onehanded|itcf_slashleft_onehanded |itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded|itcf_overswing_onehanded
									
itc_dagger  = itc_cleaver | itcf_thrust_onehanded 

itc_thrusting_sword = itcf_force_64_bits | itcf_parry_forward_onehanded| itcf_parry_up_onehanded | itcf_parry_right_onehanded |itcf_parry_left_onehanded | itcf_thrust_onehanded|itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded

itc_rapier = itcf_force_64_bits | itcf_parry_forward_onehanded| itcf_parry_up_onehanded | itcf_parry_right_onehanded |itcf_parry_left_onehanded | itcf_thrust_onehanded|itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded

itc_smallsword = itcf_force_64_bits | itcf_parry_forward_onehanded| itcf_parry_up_onehanded | itcf_parry_right_onehanded |itcf_parry_left_onehanded | itcf_thrust_onehanded|itcf_overswing_onehanded|itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded

itc_longsword = itc_dagger | itc_parry_onehanded
itc_scimitar  = itc_cleaver | itc_parry_onehanded

itc_cut_two_handed = itcf_force_64_bits | itcf_slashright_twohanded | itcf_slashleft_twohanded | itcf_overswing_twohanded | itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded									   
itc_greatsword = itc_cut_two_handed |  itcf_thrust_twohanded | itc_parry_two_handed 
itc_nodachi    = itc_cut_two_handed | itc_parry_two_handed

itc_spiked_hammer = itc_longsword

itc_bastardsword = itcf_force_64_bits | itc_cut_two_handed |  itcf_thrust_twohanded | itc_parry_two_handed | itc_longsword
itc_halfsword = itc_parry_polearm | itcf_thrust_polearm 
itc_morningstar = itc_cut_two_handed |  itc_parry_two_handed |itc_cleaver

itc_poleaxe    = itc_parry_polearm| itcf_overswing_polearm |itcf_thrust_polearm|itcf_horseback_slash_polearm
itc_staff      = itc_parry_polearm| itcf_overswing_polearm |itcf_thrust_polearm|itcf_slashright_polearm|itcf_slashleft_polearm|itcf_horseback_slash_polearm|itcf_thrust_onehanded_lance_horseback
itc_spear      = itc_parry_polearm| itcf_thrust_onehanded_lance |itcf_thrust_onehanded_lance_horseback | itcf_thrust_polearm | itcf_overswing_musket
itc_cutting_spear = itc_parry_polearm| itcf_thrust_onehanded_lance |itcf_thrust_onehanded_lance_horseback | itcf_thrust_polearm |itcf_overswing_polearm
itc_pike       = itcf_thrust_polearm | itcf_thrust_onehanded_lance 
itc_guandao    = itc_parry_polearm|itcf_overswing_polearm|itcf_thrust_polearm|itcf_slashright_polearm|itcf_slashleft_polearm|itcf_horseback_slashright_onehanded|itcf_horseback_slashleft_onehanded|itcf_horseback_slash_polearm

itc_greatlance = itcf_thrust_polearm | itcf_thrust_onehanded_lance |itcf_thrust_onehanded_lance_horseback

Compare that with Vanilla, you'll get the idea; essentially, we can build new itc_ constants here that module_items can use and the game will, surprisingly, handle mix-and-match animations and suchlike to a degree, although it's tragically inflexible about developing whole new animation states for combat state-machine stuff (like, say, making a whole new weapon attack style with custom animations) without pretty sad levels of hackery.

Also, header_constants is basically "just data"... except for the constants, especially globals, that the engine knows about.  I haven't even begun to explore fully how many of these can be eradicated; I'm guessing that less than half are subject to modder's whims in the end.  But everybody writes new globals and suchlike and defines them there.


 
Oh, and... there's the stuff that wasn't meant to be compiled / included, too, because MS expects a really specific, rigid build order so that integer references match when includes happen.  That's another fun story.  Basically, there's bits in Blood and Steel that requires two compiles to work right- the first pass builds ids that the second pass needs to be properly constructed :roll:

I wrote a lot of that years ago, so I'm not sure I should dig it out for people to laugh at my lame Python skills, but it's a thing that's done.  In my case, IIRC, it was mainly to make compiling skyboxes, flora_kinds and post_fx all fit nicely, and again IIRC, I had to do something really kludge-y to allow mission_templates to get referenced in a place it wasn't originally supposed to be, or something.  Sorry if that sounds terrifically vague, it was just so long ago I've largely forgotten what all I had to do to make that part of it work, lol.  I'll see if I can dig it up later on with some reasonable explanation of flow; hopefully I left myself some documentation, lol.
 
That's a cool use case you've detailed. Oxidsys could probably expose control for something like that if I were to place optional build script hooks in the build process.  Right now the build "pipeline" needs to generate ID tables then proceed to actually compile to MB bytecode. I could possibly add a step inbetween however where Oxidsys checks if a build script has been identified and execute it. This would probably be done through an embedded Lua interpreter, the build script would work through an API Oxidsys defines allowing you to read and alter the mods content before it's compiled to bytecode. There are some good libraries for running embedded Lua VMs in Rust already, so this probably wouldn't be too much work.


The headers that you say must be exposed, this is an issue that can be handled on a case by case basis I think. For example I could probably add an item kinds manifest for the items system that lets you work from the base item types in the game at your own peril  :lol: . Adding new races in the troop system as you pointed out is another good example. These will just have to be approached case by case I think. I certainly don't want Oxidsys to be restrictive however, so I will opt in to letting the user do more even if it means have to loosen safety a bit for them. In most cases I suspect we can probably have both though.
 
xenoargh 说:
Oh, and... there's the stuff that wasn't meant to be compiled / included, too, because MS expects a really specific, rigid build order so that integer references match when includes happen.  That's another fun story.  Basically, there's bits in Blood and Steel that requires two compiles to work right- the first pass builds ids that the second pass needs to be properly constructed :roll:

I wrote a lot of that years ago, so I'm not sure I should dig it out for people to laugh at my lame Python skills, but it's a thing that's done.  In my case, IIRC, it was mainly to make compiling skyboxes, flora_kinds and post_fx all fit nicely, and again IIRC, I had to do something really kludge-y to allow mission_templates to get referenced in a place it wasn't originally supposed to be, or something.  Sorry if that sounds terrifically vague, it was just so long ago I've largely forgotten what all I had to do to make that part of it work, lol.  I'll see if I can dig it up later on with some reasonable explanation of flow; hopefully I left myself some documentation, lol.

I've read a few cases where people have had funny issues with the build system in the native modsys, e.g. double compilation as you mention although probably for somewhat different reasons. Currently oxidsys has build targets you can specify and it just runs on that. My idea for how it'll work in the end though is that it actually has a build tree worked out. For example if you make a change in items and run
oxidsys build items
it'll recognize the bits that are dependent on items and recompile them also automatically for you. Chicken egg dilemna issues like having to build to update your ID scripts and then reference the new ID are getting eliminated because ID tables will get generated first before anything else, so you'll never have to worry about your ID being unrecognized unless it truly doesn't exist.
 
you can replace the entire coding system for sure (I have done it myself to use OO and proper coding tech and tools), so good luck with your project.

some public projects you can look:
- check the Lua version here https://forums.taleworlds.com/index.php/topic,363754.0.html

- and check Carribean! modsys. Not a great example, but they did something similar to your project, including breaking the code by file (one script, one file, etc) to make things easier on change control.

- WRECK (tools section)

you will probably find some issues on how the hardcoded stuff works, but after a few crashes and weird game behavior you will get there.

Cheers
 
The first project you mention seems to be a different beast really. Oxidsys still just compiles down to the same bytecode the native module system does. From what I can tell that Lua version is using WSE to actually get straight to the engine and skipping the bytecode step? Correct me if I'm wrong. It's a really cool project but seems to be taking a fundamentally different approach.

I can't find this Carribean! module system you mention sorry, if you have a link I'll happily check it out.

WRECK is somewhat my inspiration for doing this project, it's a proof of concept that you can build something better than the native module system, it's just while WRECK makes improvements Oxidsys is completely pulling things up by the roots.
 
Carribean! modsys comes with the game download. Did you buy it?

If you can’t find a link let me know and I can share my copy (only modsys, not the game)

Like I mentioned it is not a good example of what to do, but you can learn what not to do for sure, which helps as well lol
 
Oh wow, I had no idea that existed, it has mixed reviews but I might just purchase it anyways, thanks, I wasn't expecting something like that to exist on Steam!
 
Haugerud15328 说:
The first project you mention seems to be a different beast really.

I can't find this Carribean! module system you mention sorry, if you have a link I'll happily check it out.

WRECK is somewhat my inspiration for doing this project

to answer those comments:

1) the Lua project is to show you another iniatiate to replace the coding syntax with something that is not related to Python. Yes they also used WSE to get inside the game memory and go around the limitations of the engine API, but that is a plus.

2) already solved. But to add to it: the game comes with a PDF file explaning how to use their modsys. Like mentioned earlier I dont think it was a good change in general, but it goes in a good direction: how to break the code into smaller files and add some better organization to it. Conding projects, as a know, use different approachs to organize the layers and responsabilities in order to keep the code clean. So if you are gonna use a new scriptting system stuff like KISS, DRY, injection, low cyclomatic complexity, proper IDE support, ... can be part of your own framework. It doesnt matter how you create the .txt files after all. Be it Python, C#, Java, Javascript, or any other language, and be it a scripting language, a functional framework, a OO, ... as long it works for you, it is fine.

3) WRECK was a great project (too bad it died on version 1.0 tho). It was released a bit late on the game life cycle, so the adoption is lower than it deserves.

I would call MBScript from Warband as a version 0.5 (beta) of the scriptting language. WRECK would be close to 1.0. It is too bad the language didnt evolve to 2.0, 3.0, ... over the years. The principles are solid and it is simple enough to be used by people with no or little coding experience.

It was created to be used by game designers on the TW side, as trying to code game features in C++ would definitly limit who they would be able to hire, right? For that purpose the language itself is quite sucessful, as it has over 10 years and hundreds of projects and thousands of developers that used it at one point or another.


Also check Warband Sublime API extensions (tools section), as handy plugin to write basic MBScript.

Cheers
 
It was created to be used by game designers on the TW side, as trying to code game features in C++ would definitly limit who they would be able to hire, right? For that purpose the language itself is quite sucessful, as it has over 10 years and hundreds of projects and thousands of developers that used it at one point or another.

Yeah, while I said I don't like the native modsys, I don't think it's this way because the TW devs were dumb or anything like that. I imagine they had a few devs who started this game at a time where embedded Python or Lua was less viable, and they didn't want to spend a large amount of time on language/tool development in place of the game itself. They took a path that was probably quite clever concerning time budgeting.
 
So if you are gonna use a new scriptting system stuff like KISS, DRY, injection, low cyclomatic complexity, proper IDE support, ... can be part of your own framework. It doesnt matter how you create the .txt files after all. Be it Python, C#, Java, Javascript, or any other language, and be it a scripting language, a functional framework, a OO, ... as long it works for you, it is fine.

At first stage at least Oxid isn't going to be very fancy. I plan to add nice features to it as time goes on but for the initial stage it's mostly just an alternative grammar to what people call "MBScript". It has some small sugary conveniences in right now, but nothing hugely different. There are some things I could add I believe that would be more major e.g. a nice abstraction over arrays. It's already been discovered how to do arrays by abusing using slots, but Oxid could theoretically glaze over the part that feels like a hack. Functional paradigm goodies is another possibility. We'll see as the project grows.
 
后退
顶部 底部