5
\$\begingroup\$

I'm developing a dev console for my game. Currently I have a simple parser which is basically a regex which accepts words with dashes and dots (ie. list, 1.0, list -i) or simplistic arrays and maps (ie. [1,2,3], [x:1.0, y:2.0]). I plan on making an AST parser later on but for now this works well enough.

When these tokens get parsed first token in parsed line is the command token which is used to invoke a registered command. Commands are classes which implement a ConsoleCommand interface which looks like this (basic command pattern):

interface ConsoleCommand {
    val description: String
    val instructions: String
    fun execute(args: Array<Any>): String
}

This works, but I'm not quite satisfied since to actually register any console commands I must do so where I can access the entity. For example if I want to register the player as an interactable console command I must do so either in a custom system only created for the specific console command, or add console specific code into existing systems, basically polluting my codebase with console specific code.

My question here is what are alternative dev console implementations? Is there a way to implement a console which wouldn't affect the rest of the codebase?

I have an inkling of an idea where the console could be some sort of an observer and it itself would register onto entities and stuff, but I don't see a way of implementing that without having the entities implement some sort of special observable interface so that they can actually be observed, as well as the fact that observers usually don't manipulate the entities themselves....

\$\endgroup\$
  • \$\begingroup\$ Do you ask about game in compiled languages or the ones interpreted/with IL? \$\endgroup\$ – wondra Aug 14 '16 at 18:33
  • \$\begingroup\$ @wondra I'm not quite sure what IL interpreter is but I'm building my game in Kotlin which is a compiled language running on the JVM. I am however aware that dev consoles can be made with the same language as the rest of the game, or it can be made using scripting languages such as Lua, however I'm not yet aware of the benefits/flaws of the two approaches, nor am I aware as to how to even expose my whole engine to an outside scripting language (conceptually, not syntactically) \$\endgroup\$ – MrPlow Aug 14 '16 at 20:02
  • \$\begingroup\$ I probably asked differently: does your language have reasonable reflection support? That is probably the biggest question before you even start. \$\endgroup\$ – wondra Aug 14 '16 at 20:08
  • \$\begingroup\$ @wondra I'd say it does. It runs on the JVM and thus has the same level of reflection support as Java, which basically means that I can verify and compare types, get all methods and fields of a class, run and modify them, and probably a lot more. \$\endgroup\$ – MrPlow Aug 14 '16 at 20:51
  • \$\begingroup\$ What does the class hierarchy of your entities look like? This might be a good use case for a visitor pattern. \$\endgroup\$ – Exilyth Aug 31 '16 at 17:18
4
\$\begingroup\$

Use your integrated script engine, if you have one.

The console just provides a place where you type lines of code and prints output from scripts. No need for a new parser. No need for a new command system. No need for a custom logging/response framework.

You can often configure such scripting languages to have a custom "global context" for the console that controls which global variables exist. For instance, you might set the global object to use configuration keys as globals, so changing a setting is as easy as typing fullscreen=1. Likewise, you can automatically print the value the previous statement for languages with those semantics so that printing output is as easy as typing the variable name or calling a function.

Some languages have syntaxes that are especially friendly to these uses. Lua or JavaScript for instance are strong choices. Some languages even let you call functions without parenthesis so you type spawn "orc" instead of spawn("orc") if such things matter to you.

You can even do help systems easily in many languages. E.g. if the result of an expression is a function, print the documentation attribute attached to the function object. If an exception is thrown, you can inspect it to see which function is at the top of the stack, and extract attributes from it.

\$\endgroup\$
  • \$\begingroup\$ "The console just greediness"?? \$\endgroup\$ – user253751 Dec 18 '16 at 21:33
  • \$\begingroup\$ @immibis: haha, I must've typed this on my phone. Is it clearer now? \$\endgroup\$ – Sean Middleditch Dec 18 '16 at 23:21
0
\$\begingroup\$

I may not be understanding your question correctly, but my interpretation is as follows:

  • You have an ECS-type engine implemented in some form
  • Your entities can be interacted with via your dev console
  • You're trying to about having the console handling code strewn about randomly through your systems

My suggestion would be to add another layer of abstraction, a system that is higher level than your main subsystems (rendering, collision, etc.), and serves only to dispatch lower level messages to your entities through your usual messaging means. This doesn't really need to be a true part of your ECS, but it should do something like:

  • Allow your dev console to access a list of entities purely for dispatch (query your EntityManager if you have one?)
  • Store a list of Console commands (Command pattern)
  • Accept messages from your dev console (e.g, ConsoleCommandMessage (command,args))
  • Your ConsoleCommand object stores how the command is converted into lower level system messages, and your ConsoleSystem is responsible for dispatching those commands to the lower level systems

Based on the specifics of your implementation, you could reduce the coupling purely to the ConsoleSystem, instead of to all of the other systems in your codebase. Systems could even register their own ConsoleCommands as they're available.

Again, I'm unclear if you've already considered and ruled out this approach, but I would expect it to be a lot cleaner than the sort of coupling you're trying to avoid. You'll have to do the coupling -somewhere- in order to interact with entities, and a higher level system would let you keep that encapsulated in one spot.

\$\endgroup\$
0
\$\begingroup\$

It's easier if we look at a concrete example: telling the player to fire their weapon, via the console. To do this, the console needs to be able to:

  • Find a player (say, by entity id)
  • Tell that player to fire

But really, this is no different to what an input handler (press X to fire) or, if your player is AI-controlled, an AI routine, needs to do. Therefore if you know how to decouple these systems, you can treat the console the same way.

Two patterns helpful for this is the observer pattern or an event queue. These abstract away game entities (like players) from things that might need to access their state, or send them commands. There's no reason why your codebase needs to be polluted with console code, any more than input handling code pollutes player code, or achievement code pollutes physics code.

The only difference is that the console is sort of a global system, unlike say an input handler which is associated with one particular player. Therefore your console needs access to the global game state, so that it can see and command any game entity.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.