Haxe + Defold = ❤️

Yes, it should improve performance a bit, I am using it next way:

local msgs = require "modules.msgs"
...
msg.post("player#controller", msgs.HEALTH_DECREASE, { by: -1 })
...

Also it possible to register your go’s in some module with paths and do not use “player#controller”, something like:

local paths = require "modules.paths"

function init(self)
  paths.register("player")
end

function final(self)
  paths.remove("player")
end

and path.lua:

local M = {}
...
function M.register(name)
   M[name] = msg.url()
end

function M.remove(name)
  M[name] = nil
end
...
return M

And now we can use :

local msgs = require "modules.msgs"
local paths = require "modules.paths" --we can use one module here
...
if paths.player then --now we can check "if player exist"
  msg.post(paths.player, msgs.HEALTH_DECREASE, { by: -1 })
end
...

Of course it is just a dirty example and of course, it’s possible to do better.
But what I wanna say, When I stared with Defold I hate Lua (I have exp. with AS3, C#, Haxe, Typescript before), but after some time I found it powerful and convenient for game development.
It is possible to avoid most of your concerns just using code architecture decisions. But it needs time to found them.
But… I like how Haxe help to avoid the same problems out of the box - it’s a really powerful language and Dan did a great job. If you like Haxe and don’t wanna spend your time to learn Lua, Haxe is a great solution for you, I think.

Also, it’s possible to use TypeScript, maybe it would be interesting for you.

1 Like

You could do something similar in Defold using a Lua module:

-- message_poster.lua
local M = {}

M.HEALTH_DECREASE = hash("msgs.HEALTH_DECREASE")

function M.decrease_health(amount)
	assert(amount)
	msg.post("player#controller", M.HEALTH_DECREASE, { by: amount })
end

return M

By putting all of the message posting in a module you’d at least get code completion for the different functions for sending messages. You wrap this into an even nicer module that takes care of both posting messages and register handlers for them.

4 Likes

BTW, in case you are not aware, hot reload does not work with hxdefold. It has something to do with the lua modules.

Yes. The Defold TypeScript wrapper is a thin layer. And the widely use of any type sort of makes it less exciting. I love TypeScript and I never look back since converting to TypeScript years ago.

And I agree there are lots of ways you can structure your Lua scripts to make it better, but still it never feels as good as a static type language. Maintaining Lua is much harder, especially you have lots of people touching the code. My Dota Mod was quite hard to maintain by myself already and quickly became impossible to maintain after I several contributors joined.

Also, as you mentioned, another nice point of static type language is that it works “out of the box”. And you always feel “safe” because of Type/IntelliSense. Also you can refactor with confidence.

Well the point is not having to write the these functions by hand :slight_smile:

Anyway, it is nice to have options for every taste and I totally should actually try creating something with Defold. :slight_smile:

2 Likes

True. I played around with a module that could do something like this:

local decrease_health = message.create("decrease_health", message.number("amount"), message.string("type"))
decrease_health(".", 11, "fire") -- would result in msg.post(".", "decrease_health", { amount = 11, type = "fire" })

Not sure if it’s an improvement though? You’d get automatic input validation and message creation at least.

1 Like

Yeah, some kind of run-time schema definition/validation is what people do all the time in dynamic languages to emulate types :slight_smile: I’m not against it, but you just don’t need any of this when you use a solid statically-typed language.

Sorry, should have been more clear: I asked not what type checking is, but why you relate it in a positive way to slower feedback loop you mentioned right afterwards.

That’s what I try to explain by detailing a typo feedback loop:

Static: Make a typo -> IDE underlines it -> Fix it (the loop is within seconds)
Dynamic: Make a typo -> IDE doesn’t complain -> Run the game -> Trigger the scenario -> Blow up -> Look at the code again -> Fix attempt #1 -> Hot reload -> Nope -> Look at the code -> Fix attempt #2 -> Hot reload -> Nope -> Look at the code -> Fix attempt #3 -> Ah-hah moment -> Hot reload -> It works!

But of course, if you talk about Normal feedback loop, then of course, hot reload is the faster than having to compile and re-run the game. That’s why I like Cocos Creator: they are JS focused with Typescript, so I get the best of both worlds.

2 Likes

OK, I think I made hot-reload working :slight_smile: Since there’s always a single lua module generated by Haxe we can as well just export everything in a single global variable and that seems to work well.

PS Also I updated the APIs to 1.2.148.

3 Likes

I wonder if there’s any way to trigger the hot-reload from outside? It would be cool to do hot-reload automatically after the recompilation.

1 Like

I quickly hacked up a message-handling macro before going to bed :slight_smile:

It processes this:

@:msg function damage(state:UnitData, amount:Int) {
	// handle damage
}

@:msg function addBuff(state:UnitData, buff:Buff, duration:Int) {
	// handle buffs
}

…and automatically generates this (an excerpt from the haxe/lua output):

Unit.prototype.on_message = function(self,state,message_id,message,sender) 
  if (message_id == Unit._hxdefoldmsg_addBuff) then 
    self:addBuff(state, message.buff, message.duration);
  else
    if (message_id == Unit._hxdefoldmsg_damage) then 
      self:damage(state, message.amount);
    end;
  end;
end

-- ... and later also, for performances :-)

Unit._hxdefoldmsg_damage = _G.hash("damage");
Unit._hxdefoldmsg_addBuff = _G.hash("addBuff");

So yeah, this is definitely possible and I think is pretty cool.

4 Likes

Cool! Wouldn’t it be faster to generate on_message as table lookup instead of sequential check?

I don’t know :slight_smile: I can generate whatever best practice is :slight_smile:

@dapetcu21 did lots of magic with his https://github.com/dapetcu21/atom-defold-ide
Defold currently does not offer a public API to trigger hot reload… But there are unsupported methods that Marius used, so perhaps he can share the tribal knowledge with you.

Yeah I plan to integrate that trick for now: https://github.com/hxdefold/hxdefold/issues/11

1 Like

Unfortunately that only worked by re-building the project with bob, then triggering the engine to hot reload by calling a HTTP endpoint that dmengine exposes. I discovered that the bob build is too slow for it to be worth it. Unless we could trigger a build in the editor, which builds incrementally, it’s a bit of a no-go

Bob doesn’t clear the build folder unless you use the clean or distclean command. You should be able to do incremental builds with bob as well.

Yeah, but it still has quite a large overhead while starting up, loading the project and scanning the files in build, so that it knows what to skip. The editor incremental builds are instant. The ones with bob take 5-10 seconds from my experience.

1 Like

I’m sorry I haven’t tried it out yet, but reading your discussions I am a bit confused. If I simply change the .lua file and then trigger reload with this magic http://localhost:8001/post/@resource/reload - URL won’t it be enough to hot-reload the code?