Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full support for Mindustry v7 #74

Closed
5 tasks done
cardillan opened this issue Jan 17, 2023 · 3 comments
Closed
5 tasks done

Full support for Mindustry v7 #74

cardillan opened this issue Jan 17, 2023 · 3 comments

Comments

@cardillan
Copy link
Owner

cardillan commented Jan 17, 2023

At this moment, the code produced by Mindcode compiler is mostly Mindustry v6 compatible, although a support for the wait instruction (which is v7 specific) was already added. For a full v7 support, the following is necessary:

  • Go through instruction set and find new instructions/operations (missing lookup was already reported - Lookup function not implemented #57, but there's also stop, packcolor, some new math operations (asin, acos, atan) and perhaps more.
  • Implement support for new instructions in the compiler, update syntax documentation
  • Implement selection of target platform (V6, V7, V7 world processor)
  • Review example code to make sure it works with v7 too
  • Decide how to implement the status World processor instruction
@cardillan
Copy link
Owner Author

I'm doing an inventory of changes between v6 and v7. Generally there are these types of differences:

  • New instructions (eg. lookup, wait, packcolor) and new subtypes of instructions (eg. ucontrol unbind, ucontrol payEnter, op asin/acos/atan)
  • New arguments of instructions (ucontrol getBlock has a new output argument 'floor` -- probably returning the type of floor at given coordinates)
  • Removed instructions (ucontrol pathfind is no longer available)
  • Changes to some opcodes (control configure is control config in v7, sensor now offers @config). In both cases, it is possible to paste code to Mindustry editor with configure, which gets translated to config.
  • There's a new type of processor - World processor. It can be used to add behind-the-scenes logic to custom levels (control enemy behavior, modify the world, etc). It has a whole bunch of new instructions. I'm personally not very interested in using the world processor, but it would make much sense to support it for other users, so I definitely plan to add it.

So far I haven't found changes that would allow or require different generation of control structures, meaning that the core compiler can remain the same and only function generation will be different. To this end I plan to describe the functions and instructions using metadata and use generic mechanism based on these metadata for translating Mindcode function calls to Mindustry Logic instructions.

The question is how to deal with differing ML versions in Mindcode. I can think of there possible approaches:

  1. Specifically support the latest version of ML only. The compiled code would break backwards compatibility even where provided by ML -- specifically, we would always produce control config and there wouldn't be a way to produce control configure.) We would probably also change the function/property name in Mindcode to conform to the new name (ie. configure would have to be changed to config even in existing Mindcode sources - this isn't specifically needed and can be omitted, but generally it would make sense to keep the Mindcode nomenclature close to ML.)
  2. Fully support the latest version of ML, but use backward compatibility where provided. That means continue using configure instead of config, and allow users to use instructions from all versions (ucontrol pathfind as well as lookup, for example). It would be up to the user to use only the functions supported by the ML version he is using.
  3. Make the compiler target a specific ML version (I envision three possible values - V6, V7 Standard processor, V7 World processor). Not sure at this moment how to handle the config/configure dichotomy, but that would be worked out. This would be a compiler setting and we'd need to allow the selection in the webapp eventually.

The differences are rather small and it cannot be said that any option is decisively better than the others, in my opinion. It will set the course for future enhancements of ML too, though.

I gravitate towards the third option at the moment. Do you have any other preference regarding this issue, @francois?

@cardillan
Copy link
Owner Author

In Mindustry 7, there's a new instruction stop. We already have a function stop() mapped to ucontrol stop. For now, I'll add the new stop instruction as stopProcessor().

@cardillan
Copy link
Owner Author

cardillan commented Feb 21, 2023

The transition to V7 is pretty much finished:

  • Support for choosing target version (V6 and V7) and processor edition (Standard or World) was added to the command line compiler. When V6 is chosen, the compiler should behave exactly as before the transition. When V7 is chosen, it fully supports Mindustry Logic 7, while still being backwards-compatible with V6. The only things that won't compile in V7 mode are those that were actually removed from Mindustry 7: ucontrol pathfind, and support for rally and resupply in ulocate group argument.
  • Web app compiler currently targets V7 World processor. I plan to add support for choosing target platform in web app eventually.
  • Mapping functions to instructions is now done through metadata. The metadata looks like this (an excerpt):
        add(list, V6, V7, S, NONE, Opcode.READ,       out("result"), block("cell1"), in("at"));
        add(list, V6, V7, S, NONE, Opcode.WRITE,      in("value"),   block("cell1"), in("at"));

        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("clear"),    in("r"), in("g"), in("b"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("color"),    in("r"), in("g"), in("b"), in("a"));
        add(list, V7, V7, S, FUNC, Opcode.DRAW,       draw("col"),      in("color"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("stroke"),   in("width"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("line"),     in("x"), in("y"), in("x2"),    in("y2"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("rect"),     in("x"), in("y"), in("width"), in("height"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("lineRect"), in("x"), in("y"), in("width"), in("height"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("poly"),     in("x"), in("y"), in("sides"), in("radius"), in("rotation"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("linePoly"), in("x"), in("y"), in("sides"), in("radius"), in("rotation"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("triangle"), in("x"), in("y"), in("x2"),    in("y2"),     in("x3"), in("y3"));
        add(list, V6, V7, S, FUNC, Opcode.DRAW,       draw("image"),    in("x"), in("y"), in("image"), in("size"),   in("rotation"));

        add(list, V6, V7, S, FUNC, Opcode.PRINT,      in("what"));

        add(list, V6, V7, S, BOTH, Opcode.DRAWFLUSH,  block("display1"));
        add(list, V6, V7, S, BOTH, Opcode.PRINTFLUSH, block("message1"));

        add(list, V6, V7, S, FUNC, Opcode.GETLINK,    res("block"), in("linkNum"));

        add(list, V6, V7, S, PROP, Opcode.CONTROL,    bctrl("enabled"),   block("block"), in("value"));
        add(list, V6, V7, S, PROP, Opcode.CONTROL,    bctrl("shoot"),     block("block"), in("x"),    in("y"), in("shoot"));
        add(list, V6, V7, S, PROP, Opcode.CONTROL,    bctrl("shootp"),    block("block"), in("unit"), in("shoot"));
        add(list, V6, V6, S, PROP, Opcode.CONTROL,    bctrl("configure"), block("block"), in("value"));
        add(list, V7, V7, S, PROP, Opcode.CONTROL,    bctrl("config"),    block("block"), in("value"));
        add(list, V6, V7, S, PROP, Opcode.CONTROL,    bctrl("color"),     block("block"), in("r"), in("g"), in("b"));

Each line defines a possible configuration of an instruction. V6, V7 determines the first and last version supporting the configuration, S denotes Standard processor, NONE/FUNC/PROP/BOTH says how the mapping from a function to instruction is done, Opcode determines the opcode obviously, and the rest are definitions of arguments to the instruction. Adding new instructions is done just by adding new lines into this table. There are some exceptions still handled in the code (such as print functions allowing more than one argument).

  • A markdown document describing all possible functions and their mapping to instructions (ie. a function reference) is generated automatically for both V6 and V7 and placed into the repository. This can be linked from the Syntax document, or perhaps from the web app itself. You can find the contents of the file below.
  • A few changes were made for better V6 backward compatibility:
    • Mindustry's control configure ... instruction is control config ... in V7. The new syntax is block.config(...), but block.configure(...) is still supported.
    • ucontrol getBlock has an additional output argument (now it is ucontrol getBlock x y type building floor, floor is new). I made all output arguments that do not map to the return value of a function optional. That means getBlock(x, y, type, building) compiles in V7 as well.
  • Additionally, a few more instructions can be called using the property access syntax:
    • The radar instruction takes a turret argument - block that performs the radar operation. It seems natural to support result = turret.radar(attr1, attr2, attr3, sort, order) syntax alongside of the old (and equivalent) result = radar(attr1, attr2, attr3, sort, turret, order) one.
    • I added support for message.printflush() and display.drawflush() syntax as well, equivalent to printflush(message) and drawflush(display). I'm not entirely sure it's a good change - message or display seems to truly be a parameter to the operation. while the subject "performing" the operation the (implicit) text or graphic buffer.
  • There are a few issues with the new V7 support:
    • There's a new instruction stop, which clashes with ucontrol stop. Naturally, both of these would translate to function stop(), causing a name clash. As noted above, I resolved the issue by mapping stopProcessor() function to the stop instruction. Internally in Mindustry, the instruction is called halt. stopProcessor looks better to me, I guess it's easier to see that it maps to the stop instruction existing in the GUI. (I have no idea what stop does or how to restart the processor that was stopped.)
    • There's a new World processor instruction getblock. We have a ucontrol getBlock instruction, which result in two functions that only differ in case: getblock(...) and getBlock(...). I don't like the situation, but haven't decided on a solution. (I briefly thought about turning all world processor functions to properties of a virtual world instance, having world.getblock() instead of getblock(). There's no code using world processor instructions around as of now, so changes would still be possible.)
    • There's a World processor instruction status, which applies or clears state to/from units. In GUI, the instruction is displayed as Apply status apply ... or Apply Status clear ..., but the text representation of instruction, which directly translates to function arguments in Mindcode, is status true ... for apply and status false ... for clean. That's quite confusing. I'll either create two functions (applyStatus(...) and clearStatus(...)), or change the first parameter of the status function from true/false to apply/clear.
    • All these issues are minor and the compiler already produces valid V7 code.

Here is the full function map reference for V7 as generated automatically from metadata:

Function reference for Minustry V7

This document contains function reference for all built-in Mindcode functions. Functions are grouped by the instruction they encapsulate, so that functions with similar logic are listed together. The Mindcode source listed in the Function call column is compiled to the instruction in the Generated instruction column.

In some cases, a single instruction can be generated in more than one way (eg. the radar instruction, which can be written as a turret.radar function, or as a radar function which takes turret as a parameter). Both ways are identical. Additionally, some functions have optional parameters, which are marked by a question mark (eg. building?). Only output parameters are optional, and you may omit them if you don't need the value they return. When omitted, the optional parameter is replaced by an unused temporary variable. Mindcode allows you to omit all optional argument, but in this case the entire instruction will be recognized as unused and may be removed by the optimizer.

Micro Processor, Logic Processor and Hyper Processor

Instruction draw

Add an operation to the drawing buffer. Does not display anything until drawflush is used.

Function call                                                                                 Generated instruction                                                  
clear(r, g, b) draw clear r g b 0 0 0
color(r, g, b, a) draw color r g b a 0 0
col(color) draw col color 0 0 0 0 0
stroke(width) draw stroke width 0 0 0 0 0
line(x, y, x2, y2) draw line x y x2 y2 0 0
rect(x, y, width, height) draw rect x y width height 0 0
lineRect(x, y, width, height) draw lineRect x y width height 0 0
poly(x, y, sides, radius, rotation) draw poly x y sides radius rotation 0
linePoly(x, y, sides, radius, rotation) draw linePoly x y sides radius rotation 0
triangle(x, y, x2, y2, x3, y3) draw triangle x y x2 y2 x3 y3
image(x, y, image, size, rotation) draw image x y image size rotation 0

Instruction print

Add text to the print buffer. Does not display anything until printflush is used.

Function call                                                                                 Generated instruction                                                  
print(what) print what

Instruction drawflush

Flush queued Draw operations to a display.

Function call                                                                                 Generated instruction                                                  
display1.drawflush() drawflush display1
drawflush(display1) drawflush display1

Instruction printflush

Flush queued Print operations to a message block.

Function call                                                                                 Generated instruction                                                  
message1.printflush() printflush message1
printflush(message1) printflush message1

Instruction getlink

Get a processor link by index. Starts at 0.

Function call                                                                                 Generated instruction                                                  
block = getlink(linkNum) getlink block linkNum

Instruction control

Control a building.

Function call                                                                                 Generated instruction                                                  
block.enabled(value) control enabled block value 0 0 0
block.shoot(x, y, shoot) control shoot block x y shoot 0
block.shootp(unit, shoot) control shootp block unit shoot 0 0
block.configure(value)
Deprecated. Use config instead.
control config block value 0 0 0
block.config(value) control config block value 0 0 0
block.color(r, g, b) control color block r g b 0

Instruction radar

Locate units around a building with range.

Function call                                                                                 Generated instruction                                                  
result = turret.radar(attr1, attr2, attr3, sort, order) radar attr1 attr2 attr3 sort turret order result
result = radar(attr1, attr2, attr3, sort, turret, order) radar attr1 attr2 attr3 sort turret order result

Instruction sensor

Get data from a building or unit.

Function call                                                                                 Generated instruction                                                  
result = sensor(object, property) sensor result object property

Instruction op

Perform an operation on 1-2 variables.

Function call                                                                                 Generated instruction                                                  
result = max(a, b) op max result a b
result = min(a, b) op min result a b
result = angle(a, b) op angle result a b
result = len(a, b) op len result a b
result = noise(a, b) op noise result a b
result = abs(a) op abs result a 0
result = log(a) op log result a 0
result = log10(a) op log10 result a 0
result = floor(a) op floor result a 0
result = ceil(a) op ceil result a 0
result = sqrt(a) op sqrt result a 0
result = rand(a) op rand result a 0
result = sin(a) op sin result a 0
result = cos(a) op cos result a 0
result = tan(a) op tan result a 0
result = asin(a) op asin result a 0
result = acos(a) op acos result a 0
result = atan(a) op atan result a 0

Instruction lookup

Look up an item/liquid/unit/block type by ID. Total counts of each type can be accessed with @unitcount, @itemCount, @liquidCount, @blockCount.

Function call                                                                                 Generated instruction                                                  
result = lookup(type, index) lookup type result index

Instruction packcolor

Pack [0, 1] RGBA components into a single number for drawing or rule-setting.

Function call                                                                                 Generated instruction                                                  
result = packcolor(r, g, b, a) packcolor result r g b a

Instruction wait

Wait a certain number of seconds.

Function call                                                                                 Generated instruction                                                  
wait(sec) wait sec

Instruction stop

Halt execution of this processor.

Function call                                                                                 Generated instruction                                                  
stopProcessor() stop

Instruction end

Jump to the top of the instruction stack.

Function call                                                                                 Generated instruction                                                  
end() end

Instruction ubind

Bind to the next unit of a type, and store it in @Unit.

Function call                                                                                 Generated instruction                                                  
ubind(type) ubind type

Instruction ucontrol

Control the currently bound unit.

Function call                                                                                 Generated instruction                                                  
idle() ucontrol idle 0 0 0 0 0
stop() ucontrol stop 0 0 0 0 0
move(x, y) ucontrol move x y 0 0 0
approach(x, y, radius) ucontrol approach x y radius 0 0
boost(enable) ucontrol boost enable 0 0 0 0
target(x, y, shoot) ucontrol target x y shoot 0 0
targetp(unit, shoot) ucontrol targetp unit shoot 0 0 0
itemDrop(to, amount) ucontrol itemDrop to amount 0 0 0
itemTake(from, item, amount) ucontrol itemTake from item amount 0 0
payDrop() ucontrol payDrop 0 0 0 0 0
payTake(takeUnits) ucontrol payTake takeUnits 0 0 0 0
payEnter() ucontrol payEnter 0 0 0 0 0
mine(x, y) ucontrol mine x y 0 0 0
flag(value) ucontrol flag value 0 0 0 0
build(x, y, block, rotation, config) ucontrol build x y block rotation config
getBlock(x, y, type?, building?, floor?) ucontrol getBlock x y type building floor
result = within(x, y, radius) ucontrol within x y radius result 0
unbind() ucontrol unbind 0 0 0 0 0

Instruction uradar

Locate units around the currently bound unit.

Function call                                                                                 Generated instruction                                                  
result = uradar(attr1, attr2, attr3, sort, order) uradar attr1 attr2 attr3 sort 0 order result

Instruction ulocate

Locate a specific type of position/building anywhere on the map. Requires a bound unit.

Function call                                                                                 Generated instruction                                                  
found = ulocate(ore, oreType, outx?, outy?) ulocate ore core true oreType outx outy found __tmp0
found = ulocate(building, group, enemy, outx?, outy?, building?) ulocate building group enemy @copper outx outy found building
found = ulocate(spawn, outx?, outy?, building?) ulocate spawn core true @copper outx outy found building
found = ulocate(damaged, outx?, outy?, building?) ulocate damaged core true @copper outx outy found building

World processor

These instructions are only available to the World Processor,
which can be placed in custom-created levels in Mindustry 7.

Instruction getblock

Get tile data at any location.

Function call                                                                                 Generated instruction                                                  
result = getblock(layer, x, y) getblock layer result x y

Instruction setblock

Set tile data at any location.

Function call                                                                                 Generated instruction                                                  
setblock(floor, to, x, y) setblock floor to x y 0 0
setblock(ore, to, x, y) setblock ore to x y 0 0
setblock(block, to, x, y, team, rotation) setblock block to x y team rotation

Instruction spawn

Spawn unit at a location.

Function call                                                                                 Generated instruction                                                  
result = spawn(unit, x, y, rotation, team) spawn unit x y rotation team result

Instruction status

Apply or clear a status effect from a unit.

Function call                                                                                 Generated instruction                                                  
status(false, status, unit, duration) status false status unit duration
status(true, status, unit) status true status unit 0

Instruction spawnwave

Spawn a wave.

Function call                                                                                 Generated instruction                                                  
spawnwave(x, y, natural) spawnwave x y natural

Instruction setrule

Set a game rule.

Function call                                                                                 Generated instruction                                                  
setrule(currentWaveTime, value) setrule currentWaveTime value 0 0 0 0
setrule(waveTimer, value) setrule waveTimer value 0 0 0 0
setrule(waves, value) setrule waves value 0 0 0 0
setrule(wave, value) setrule wave value 0 0 0 0
setrule(waveSpacing, value) setrule waveSpacing value 0 0 0 0
setrule(waveSending, value) setrule waveSending value 0 0 0 0
setrule(attackMode, value) setrule attackMode value 0 0 0 0
setrule(enemyCoreBuildRadius, value) setrule enemyCoreBuildRadius value 0 0 0 0
setrule(dropZoneRadius, value) setrule dropZoneRadius value 0 0 0 0
setrule(unitCap, value) setrule unitCap value 0 0 0 0
setrule(mapArea, x, y, width, height) setrule mapArea 0 x y width height
setrule(lighting, value) setrule lighting value 0 0 0 0
setrule(ambientLight, value) setrule ambientLight value 0 0 0 0
setrule(solarMultiplier, value) setrule solarMultiplier value 0 0 0 0
setrule(buildSpeed, value, team) setrule buildSpeed value team 0 0 0
setrule(unitBuildSpeed, value, team) setrule unitBuildSpeed value team 0 0 0
setrule(unitCost, value, team) setrule unitCost value team 0 0 0
setrule(unitDamage, value, team) setrule unitDamage value team 0 0 0
setrule(blockHealth, value, team) setrule blockHealth value team 0 0 0
setrule(blockDamage, value, team) setrule blockDamage value team 0 0 0
setrule(rtsMinWeight, value, team) setrule rtsMinWeight value team 0 0 0
setrule(rtsMinSquad, value, team) setrule rtsMinSquad value team 0 0 0

Instruction message

Display a message on the screen from the text buffer. Will wait until the previous message finishes.

Function call                                                                                 Generated instruction                                                  
message(notify) message notify 0
message(mission) message mission 0
message(announce, duration) message announce duration
message(toast, duration) message toast duration

Instruction cutscene

Manipulate the player camera.

Function call                                                                                 Generated instruction                                                  
cutscene(pan, x, y, speed) cutscene pan x y speed 0
cutscene(zoom, level) cutscene zoom level 0 0 0
cutscene(stop) cutscene stop 0 0 0 0

Instruction explosion

Create an explosion at a location.

Function call                                                                                 Generated instruction                                                  
explosion(team, x, y, radius, damage, air, ground, pierce) explosion team x y radius damage air ground pierce

Instruction setrate

Set processor execution speed in instructions/tick.

Function call                                                                                 Generated instruction                                                  
setrate(ipt) setrate ipt

Instruction fetch

Lookup units, cores, players or buildings by index. Indices start at 0 and end at their returned count.

Function call                                                                                 Generated instruction                                                  
result = fetch(unitCount, team) fetch unitCount result team 0 0
result = fetch(playerCount, team) fetch playerCount result team 0 0
result = fetch(coreCount, team) fetch coreCount result team 0 0
result = fetch(buildCount, team, type) fetch buildCount result team 0 type
result = fetch(unit, team, index) fetch unit result team index 0
result = fetch(player, team, index) fetch player result team index 0
result = fetch(core, team, index) fetch core result team index 0
result = fetch(build, team, index, type) fetch build result team index type

Instruction getflag

Set a global flag that can be read by all processors.

Function call                                                                                 Generated instruction                                                  
result = getflag(flag) getflag result flag

Instruction setflag

Check if a global flag is set.

Function call                                                                                 Generated instruction                                                  
setflag(flag, value) setflag flag value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant