#๐Ÿ”’ Game design choices to help avoid future refactoring

147 messages ยท Page 1 of 1 (latest)

pale pecan
#

Im making a text based game and im stuck designing a very important part;
What you need to know:

sharp latchBOT
#

@pale pecan

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

pale pecan
# pale pecan Im making a text based game and im stuck designing a very important part; What y...

Im making the server side so we dont talk about the client part here because that only shows stuff and dosent do any of the actual logic; when a player sends an update, it gets picked up by an async function, like async def a(event):, this async function reality checks the event to see whether the client is lying or not, basic server stuff (the event contains player id and their request), this is where im stuck, i dont know how to handle the event, heres what i have tried so far:

(the players data is loaded into memory, and each player has their own instance of the Player class holding their data which functions access)

Making a decorator, and putting it on top of every function, eg @catch('CRAFT_ITEM') this one for example catches any craft item request and gives it to the function below it, the function does its job and calls another function to tell the player that the craft was successful (noodle like code, all tangled)
client > a(event) > craft_item > return function

Another approach that i tried is making methods under Player class, like Player.craft etc.. this was my best approach so far, async def a(event) passes event to Player instance of the player who requested the thing (after validating the request), then Player does its stuff, and calls another method called self.return to return whether the request was successful or not (did the item get crafted or not) , but the problem with this design is that the game has too too too many stuff related to players, that if i want to put all of them under the player class, it will become a huge uncleanable mess
client > a(event) > Player > craft_item method > self.return > client

#

.
My latest and best approach: using best of both methods above, making stuff like craft_item their own seprate functions, and putting the @catch handler inside player class, this means a(event) gets the event, passes it to the players instance who requested it, and the player class will call the required function (eg craft_item) to craft the item, then craft_item returns the response, and player class uses self.return to acknowledge the player
client > a(event) > Player > craft_item > Player > client

Now, my question here is that can this be designed better maybe? or if my latest design has any flaws?

stoic kernel
pale pecan
stoic kernel
#

i see i see

pale pecan
# stoic kernel ah so like dank memer

idk about that one but yea you get the idea, ask any questions you have that may help you get a clearer vision of what it is, so you can help me better

stoic kernel
sharp latchBOT
pale pecan
#
  1. because events come from buttons and buttons are easier than writing a whole command
  2. both commands and events will be a text, like /craft stick 5 or CRAFT_STICK_5
stoic kernel
pale pecan
#

wdym by that

stoic kernel
#

you could probably use dpy's arg parser on your telegram inputs

pale pecan
#

i think you mean something like input.split('_') and use different parts?

stoic kernel
# pale pecan wdym by that

well if i was doing it, i'd use a /craft stick 5 layout and then use dpy to add a command: ```py
@bot.command()
async def craft(item: str, amount: int):
...


now when you get the text from your telegram message, you can run `bot.process_commands` on it to get it to find and run the right command
pale pecan
#

My main problem is how to process the data not how to structure the data

#

What i mean: i gave examples like client > a(event) > Player > craft_item > Player > client or client > a(event) > Player > craft_item method > self.return > client etc in my explanation, i want to find a good way to handle where the event goes

#

Also please ping me when you respond im in another tab analyzing my code

stoic kernel
#

wait wdym "self.return"

pale pecan
#

in my explanations self.return is a method under Player class

#

when i mentioned 'self' before return, this means the function is getting ran inside the Player class

#

return means return data to the users client

#

update the user client for new changes

pale pecan
#

and thats what self means, any other questions my friend?

stoic kernel
pale pecan
pale pecan
#

i will, its just a name for demonstration

stoic kernel
pale pecan
stoic kernel
#

do you have any source code so far btw? i want to see an example if you have any

pale pecan
pale pecan
#
_event_handlers_registry = {}


def catch(pattern=None):
    def wrapper(func):
        _event_handlers_registry[pattern] = func
        return func

    return wrapper


async def mainCallbackHandler(
    event
):
    func = _event_handlers_registry[event.pattern]
    await func(event)

just to demonstrate

#

its an sketch @stoic kernel

#

i feel like im missing a use of OOP in this part of the game, which is probably the most important part

stoic kernel
#

i have no idea what anything in this snippet is meant to be

pale pecan
#

every function that has the @catch decorator on top will get recorded

#

like @catch('craft_item')

#

and when mainCallbackHandler receives an update (its name async def a(event) in my explanations)
it will tie the event to its function and call the function

stoic kernel
pale pecan
#

it makes functions listen for events

stoic kernel
#
@bot.listen('my_event')
async def whatever():
    print("i got called!")

bot.dispatch("my_event") # whatever() gets called
pale pecan
pale pecan
stoic kernel
#

again, i still think a command-based approach would be more flexible, but i have no idea what your inputs look like (mostly because you havent really typed anything)

pale pecan
#

its litteraly the same trust me

#

and the problem isnt even this

stoic kernel
pale pecan
#

comamnd is a text

#

my events are a text

#

its the same

#

and im impleneting the same so called @bot.listen('my_event')

stoic kernel
pale pecan
#

what makes you say that

stoic kernel
# pale pecan what makes you say that

because commands are different from events

commands are text that gets parsed into a specific designated function, which is then given any remaining parsed args

events are simply just things that get dispatched when something happens

stoic kernel
#

you can extend a command easily by just adding another parameter. adding something else to a payload in an event requires you to update every dispatch call of that event. they're just not the same

pale pecan
#

but they are same as commands

#

they are same as the commands are text that gets parsed into a specific designated function, which is then given any remaining parsed args trust me

stoic kernel
#

what

pale pecan
#

i just called them events because they are commands that come from buttons

#

just trust me and lets continue

stoic kernel
pale pecan
#

thank you.

pale pecan
cerulean cliff
# pale pecan Im making the server side so we dont talk about the client part here because tha...

Here are my 2 cents:

Both of the ideas you suggested are roughly the same with the only difference being whether you use OOP or not. I think I would prefer the latter since it could reduce the amount of boilerplate required (looking up user ID, getting specific fields, etc.)

Another thing you could consider is the response method, since using a single function for that means you need to pass in all the request details which your processing functions like craft_item shouldn't care about. Instead, I would wrap those in an Event object and pass them into other functions which can use a method like event.respond(...), similar to how discord.Context works.

pale pecan
cerulean cliff
#

"latter" = the second option
"boilerplate" = repetitive code

pale pecan
cerulean cliff
#

I don't think it's necessary to draw distinctions between events/commands

#

to the server, they mean the same thing

pale pecan
pale pecan
cerulean cliff
#

so there's an event for each craftable item type?

pale pecan
#

no i have 2 catchers, one is exact match and the other one is pattern match

#

exact match: GOTO_CAVE

#

pattern match CRAFT__

#

twait

#

CRAFT_*_*

#

now better

cerulean cliff
#

right

#

I would rather separate on spaces and use match/case but let's not get distracted

pale pecan
#

i use that so i can use hash map from dicts to find events at o(1) speed
thats not really the concern/problem right now

#

the problem is how you handle the events, think of all of them as exact events, not pattern because that opens whole another world which i have already solved for myself

cerulean cliff
pale pecan
#

no

#

in the approach you replied to, player class only has like 2-3 methods

pale pecan
#

read what ive wrote under it client > a(event) > Player > craft_item > Player > client

cerulean cliff
#

oh I see

#

so similar to @bot.command() in d.py

pale pecan
#

Yep

cerulean cliff
#

I think that's also a reasonable approach

pale pecan
#

What i wanted help for:

Any better design ideas that can override my exisitng ones and be generally better
Or, an approval of my own design that it wont run into problems later on

cerulean cliff
#

the only drawback I see is that commands must be loaded after the Player object is created

#

so I would move all this to a separate EventHandler class (or similar)

cerulean cliff
#

nevermind, I think it depends on implementation

pale pecan
cerulean cliff
#

you might want to put all of this in a class, but otherwise I don't see any issues with it

cerulean cliff
pale pecan
#

what are you reffering to by "all of this"? the event handler? the functions ?

pale pecan
cerulean cliff
#

decorator + events registry + handle function

pale pecan
#

the game has "servers" no the server that is the server-side, servers like, different lobbies

each lobby is an instance of the Game class

#

the Game class has every area in that lobby loaded, caves, mountains, village, etc..

#

and whenever something happens it sends an update to Player class to update the users ui based on the events

#

and whenever the player for example requests to mine a block, player class tells game class, game class responds with an update

cerulean cliff
#

might be worth looking into if you want modularity

pale pecan
pale pecan
cerulean cliff
#

if you want to group a bunch of commands together, you can create a cog like this: ```py
class MyCog(commands.Cog):
... # init and stuff

@commands.command()
def command1(...): ...

@commands.command()
def command2(...): ...
Then load the cog, which adds those commands to your bot: ```py
bot.add_cog(MyCog())
pale pecan
#

oh

cerulean cliff
#

the magic is that you can put cogs in separate files which helps logically split your code

pale pecan
#

understood, thanks

no my commands aka events are just functions like

#

@catch('craft')
def craft

catch sell
def sell

catch forge
def forge

cerulean cliff
#

oh right, you were making your own dispatcher

pale pecan
cerulean cliff
#

I think it's worth a try

pale pecan
#

any other suggestions? or any questions ?

cerulean cliff
#

glhf :)

pale pecan
#

tyvm bro appreciate the time you put to help โค๏ธ

#

have a great day !

cerulean cliff
#

no problem

#

you too

pale pecan
#

!close

sharp latchBOT
#
Python help channel closed with !close

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.