• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Source's custom OT game engine (TypeScriptFTW)

danilopucci

Member
Joined
Nov 22, 2019
Messages
13
Reaction score
11
Great project! I see you are interested about be a better coder... Well, a great source to guide your way is Clean Code by Uncle Bob (Robert Cecil Martin) and The Mythical Man-Month by Fred Brooks! These books are essential for any developer.

By the way, do you have any details on how to read the content of monster.db file? I never tried it too much but it any tip for me would be welcome!

Good luck on your project, keep coding!
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
By the way, do you have any details on how to read the content of monster.db file? I never tried it too much but it any tip for me would be welcome!
It is what it says in the file.
Code:
# Race     X     Y  Z Radius Amount Regen.

# ====== 0997,0988,09 ====================
    50 31927 31622  9     50      3    700
Each spawn is in a seperate line. Race number (50 = orc spearman in this case), coordinates of the spawn center (31927,31622,9), radius for antiluring pooff (50 squares), number of monsters (3) and respawn time base (700) which is later used in a hardcoded formula to determine the real respawn time.
Numbers starting with '#' are ignored, but they contain information of which sector the below spawns are in.
 
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
Scenario:

You've just teleported, and 3 players have trapped a demon in the corner.

How are you going to receive all the data about the demon, as it hasn't called gameMoveCreature()?
gameMoveCreature(...) only tracks movements. If you want to do something else with a monster, player, map, or anything else, you do it in the async gameLoop, just like how CIP-engine did it, and TFS probably has its own similar system.
You have to be more specific to get a specific answer, you haven't actually specified what you want to do with the demon or why you want to access this demon's data or object, what do you want to do with it?
If you want to do something to all monsters that are trapped in corners, you check in the walk function if the creature walking is a monster or NPC or whatever you want to select, then after changing position you check if you're surrounded by tiles that cannot be walked through or that the tile is blockable (in my engine the tile has a blockable flag so it's trivial to check).

Are you firing the event upon spawn? or upon waking up? Please don't tell me all you're monsters are constantly moving with no spectators. And you can just have random slightly different think/move intervals to make the creatures think independently of each other.
You could do either of those things, in the async gameLoop, or upon any other game event, such as creature movement.

And for your second point, please tell me the engines that don't have random walking if no target, and what do they use if not, a pattern?
Come to think about it I forgot to mention a detail, I was talking/thinking about monster movement randomization when on a GM or as a player on another floor.
It's true that engines has since forever had monster movement randomization when there's specifically a player around that is untargetable on the same floor.
But there was no random monster movement on different floors or when around on a GM, and the movement algorithm has always been off, I'm still not sure if there's any other server that's implemented the original cip randomized monster movement with the proper sleeptimes and such, though I wouldn't be surprised if there has been as it was not very hard to implement.
 
Last edited:

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,487
Solutions
49
Reaction score
1,583
Location
Poland
GitHub
Oen44
gameMoveCreature(...) only tracks movements. If you want to do something else with a monster, player, map, or anything else, you do it in the async gameLoop, just like how CIP-engine did it, and TFS probably has its own similar system.
You have to be more specific to get a specific answer, you haven't actually asked what you want to do with the demon or why you want to access this demon's data or object, what do you want to do with it?
In other words the answer to your question depends on what the conditions you want to be for selecting the demon and then what to do with it.
If you want to do something to all monsters that are trapped in corners, you check in the walk function if the creature walking is a monster or NPC or whatever you want to select, then after changing position you check if you're surrounded by tiles that cannot be walked through or that the tile is blockable (in my engine the tile has a blockable flag so it's trivial to check).
He is asking the same question that I am.
You are saying that when creature moves, it is synced with players around it, that makes sense. If the player TELEPORTS (not moves) to a different part of the world which is equivalent of what happens when you login to the world, then what function do you use to get all the monsters/npcs/players that are on these tiles? Because that's where getSpectators plays the main part.
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
Come to think about it I forgot to mention a detail, I was talking/thinking about monster movement randomization when on a GM or as a player on another floor.
It's true that engines has since forever had monster movement randomization when there's specifically a player around that is untargetable on the same floor.
But there was no random monster movement on different floors
Because that's how it used to be in Tibia as well, prior to 7.5 version. Monsters didn't move when there were no players on the same floor nearby. It was changed mostly to prevent around hole traps and such that people complained about back in the days. But they still need a spectator to be moving, otherwise they stand still and so do NPCs.
 

danilopucci

Member
Joined
Nov 22, 2019
Messages
13
Reaction score
11
It is what it says in the file.
Code:
# Race     X     Y  Z Radius Amount Regen.

# ====== 0997,0988,09 ====================
    50 31927 31622  9     50      3    700
Each spawn is in a seperate line. Race number (50 = orc spearman in this case), coordinates of the spawn center (31927,31622,9), radius for antiluring pooff (50 squares), number of monsters (3) and respawn time base (700) which is later used in a hardcoded formula to determine the real respawn time.
Numbers starting with '#' are ignored, but they contain information of which sector the below spawns are in.
Shame on me, I was with some crazy version of tibia-cip files. I had a monster.db with 38KB which looks like a binary file. Now I checked the version from otland and yeah, it is a human-readable file.
Thank you so much anyway!
 
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
He is asking the same question that I am.
You are saying that when creature moves, it is synced with players around it, that makes sense. If the player TELEPORTS (not moves) to a different part of the world which is equivalent of what happens when you login to the world, then what function do you use to get all the monsters/npcs/players that are on these tiles? Because that's where getSpectators plays the main part.
gameGetPlayers(...) lol.

I haven't added the code to send to nearby players that a player is logging in yet, but teleport walks like normal.
All of these game... functions such as gameGetPlayers (the base function) are a pretty recent development, I tried to implement them earlier but failed, more recently I've started working on it to try to get multiplayer functionality working.

Sending to the other players that a player has teleported in is pretty trivial though and I'll be using gameGetPlayers(...) to get all the players surrounding the tile that the player is spawning in on.

Thinking about it now I was thinking about implementing this in the last few days but was thinking I'd have to login to another OTS and inspect the network packet used to add the creature, but now that I think about it I should already have this implemented in single player mode, but it's already implemented in sendAddTileThing(...) for single player use.
I'll just have to create a new function gameAddObject and use that for player login.
Edit: I already had the function implemented including sending the data to all the players.
Appreciate the input.
 
Last edited:

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,487
Solutions
49
Reaction score
1,583
Location
Poland
GitHub
Oen44
gameGetPlayers(...) lol.
Oh boi... either start naming your functions properly or read our posts few more times. Is this gameGetPlayers function returning spectators that are NOT players?
 
Last edited:
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
Because that's how it used to be in Tibia as well, prior to 7.5 version. Monsters didn't move when there were no players on the same floor nearby. It was changed mostly to prevent around hole traps and such that people complained about back in the days. But they still need a spectator to be moving, otherwise they stand still and so do NPCs.
Sure, I'm talking about OTS versions 7.6+, I'm sure this was a long time ago though, like about a decade or so ago, I believe most if not all servers back then had no randomized monster movement when no players were around on the same floor, at least 7.6-7.72 servers, though I could be misremembering.

Yeah for sure.
Post automatically merged:

Oh boi... either start naming your functions properly or ready our posts few more times. Is this gameGetPlayers function returning spectators that are NOT players?
No, I only need to get players because only players has sockets, lol.
The monster is walking so its coordinates are sent to gameGetPlayers(...).
gameGetPlayers(...) then loops through players and checks if any of the players are within the same range of coordinates that was supplied to the function through its arguments.
I take your confusion as evidence that TFS' getSpectators(...) code, if at all used for the purposes discussed, need to be simpler so people can wrap their head around it, lol.

Edit: I think maybe I realize what you're alluding to though.
For instance how would a monster know to melee attack a player. How does the monster know that the player is in proximity?
What first comes to mind is again the gameLoop function that's run at 50hz (same as CIP). From what I've read this is how CIP-engine does it.
The other option is to check upon every movement if a targeted creature is in proximity.
This can be done in a loop by simply checking the 9 proximate tiles for a melee attack to trigger (in both directions).
An option in the second case would then be to either add a gameGetCreatures(...) or simply getCreatures(...) function that works as a modified version of gameGetPlayers with an argument for what creature types to include, simply for convenience instead of writing separate code in the monster walking movement, this assumes two things though, that 1) it's equal, proximate, or negligible performance-wise to use this function over manually checking, and 2) that the function can be used for other purposes than melee attacks, otherwise it's not necessary or useful to create a separate function for this.
 
Last edited:

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
No, I only need to get players because only players has sockets, lol.
The monster is walking so its coordinates are sent to gameGetPlayers(...).
gameGetPlayers(...) then loops through players and checks if any of the players are within the same range of coordinates that was supplied to the function through its arguments.
I take your confusion as evidence that TFS' getSpectators(...) code, if at all used for the purposes discussed, need to be simpler so people can wrap their head around it, lol.
As I understand oen's asking, how does HE (the player that's being teleported) get to know all the creatures in the place he's teleported to. Pretty much the same scenario as when you log in and need to receive the whole map with all creatures. NOT how do the others get to know about your login/teleport, cause in this case you obviously only need to notify players.
 
Last edited:

drakylucas

Well-Known Member
Joined
Dec 15, 2015
Messages
193
Solutions
4
Reaction score
87
As I understand oen's asking, how does HE (the player that's being teleported) gets to know all the creatures in the place he's teleported to. Pretty much the same scenario as when you log in.
Reading the topic, I can only imagine that, after being teleported, the player would call a function that iterate over all creatures of the world (monsters, npcs, other players) and check the distance between their positions and player position. If the player is close to the creature, it sends the information from the creature through his sockets/network functions.

Instead of iterating X tiles close to the player, I believe it iterates over all creatures and check if each of them is close to the player position.


Am I right, @Source?

Please give more details about how it should work... When a player moves, what exactly is called in your engine to update everything (tiles, creatures, magic effects, items on ground, etc) to the client?
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
Reading the topic, I can only imagine that, after being teleported, the player would call a function that iterate over all creatures of the world (monsters, npcs, other players) and check the distance between their positions and player position. If the player is close to the creature, it sends the information from the creature through his sockets/network functions.

Instead of iterating X tiles close to the player, I believe it iterates over all creatures and check if each of them is close to the player position.
That's how I understood it too, but it doesn't make sense and neither does using "getSpectators" to get ALL creatures in this case.
Since you need to receive info about the whole map-part, you're already going to iterate through squares to get all the visible objects anyway. Just treat creatures like other objects. Then you need to get (only) players to notify them separately (the list of players you need to receive isn't necessairly the same as the list of players your character needs to be sent to).
 
Last edited:
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
As I understand oen's asking, how does HE (the player that's being teleported) gets to know all the creatures in the place he's teleported to. Pretty much the same scenario as when you log in and need to receive the whole map with all creatures. NOT how do the others get to know about your login/teleport, cause in this case you obviously only need to notify players.
Oh right. That's all tile data so that's done in the world (map) class. You have to loop over tiles here, so tile data and creatures are sent here.
I don't necessarily want to reveal how I store map and creature data without using binary trees, but all I can say is that the creatureid is easily and quickly retrieved and then the creature object is fetched from a map using that creatureid.
And yeah, if someone's able to guess it I might do a reveal, lol.

Reading the topic, I can only imagine that, after being teleported, the player would call a function that iterate over all creatures of the world (monsters, npcs, other players) and check the distance between their positions and player position.
Nah, I didn't realize he was talking about tile data (assuming he was) which is something completely different entirely, there's no need for getSpectators(...) or anything there since adding the creature data is a trivial amount of code.
Post automatically merged:

That how I understood it too, but it doesn't make sense and neither does using getSpectators to get ALL creatures.
Since you need to receive info about the whole map-part, you're already going to iterate through squares to get all the visible objects anyway. Just treat creatures like other objects. Then you need to get (only) players separately to notify them.
Exactly, it made no sense to me either, I thought he was just talking about creatures moving, like he brought that up over and over again, but it could make sense that he was trying to talk about fetching map data yeah.
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
Ye, you need to iterate through squares to get their data, up to 10 objects, where creatures are also objects.
So you're not going to need all creatures in range. There can be 100 stacked creatures and there is no point to get them all just as you're not getting the whole stack of milion items.
You also don't need to get all creatures to notify them about your appearance (only players that can see you).
Using getSpectators to get ALL creatures in range in this scenario is pointless. So is it for spells as it was suggested by @oen432 , since - again - you need to iterate through squares affected by the spell + get players that should see the spells effect.
 
Last edited:
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
Ye, you need to iterate through squares to get their data, up to 10 objects, where creatures are also objects.
So you're not going to need all creatures in range. There can be 100 stacked creatures and there is no point to get them all just as you're not getting the whole stack of milion items.
You also don't need to get all creatures to notify them about your appearance (only players that can see you).
Using getSpectators to get ALL creatures in range in this scenario is pointless. So is it for spells as it was suggested before, since - again - you need to iterate through squares affected by the spell + get players that should see the spells effect.
Yeah, there's no point getting all of them at once anyways, you need to iterate over each tile and any creatures on that specific square, no other square, until you're moving on to the next one and then you can look for creatures there, it's just a matter of order.

I feel like it could have worked for spells/runes if their shapes were all just squares and long "I" shaped blocks like in tetris (without any of the wiggly shapes).
 

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
Not in your approach. But you could notify all creatures there, where for monsters and NPCs it would mean changing their state (waking up) and then chaining them, so that later you don't have to iterate over and call all non player creatures to do actions (move, etc.) every round, like I think you do now(?). You said that you could easily add another loop through monsters list aside of your getPlayers function for that, but then it's better to search through the map for creatures than iterate all creatures and calculate the distance.
Speaking of Cip engine, you may want to take a look at their TFindCreatures class and ProcessCreatures function.
 
Last edited:

Boy67

Intermediate OT User
Joined
Jul 4, 2007
Messages
302
Solutions
12
Reaction score
125
Location
England
I think you are still missing the point we were trying to make.

getSpectators searches the map data surrounding the player. It doesn't iterate through all players, creatures and npcs like some user suggested earlier as that would be pure overkill. Imagine if there are 200 players, a total spawn count of 10,000 and 300 npc's. Why loop through 10,500 entities and compare their distance with the player? xD

It's much easier to check 15x11 (165 tiles), and fetch all players, creatures and npcs within that range, and grab their data.

Now, what do we do then? Imagine this scenario.

You've just logged in, there are several creatures, players and NPC's that the client needs data for. What HP do they all have? What is their looktype/outfit, name, position etc. We can call getSpectators and grab all this information, and at the same time, we can wake up the creatures and npcs if they are sleeping.

You can't rely on getting initial data from a creature on them moving, because if you've just logged in and they are trapped in a corner, not moving, you won't get any data until they do.

Now, you argued that you could have separate functions for each, and just keep getSpectators for players only. So you will make 2 more similar functions to grab the creature and npc data? Instead of looping through 15x11 once, you're going to be doing it 3 times....

You also have to consider, that you aren't going to be just check x and y for all floors, so you will have to set a min and max floor. If you are underground you dont need to know about all the creatures above +1, and if youre above ground you dont need to get data for anything below -1.

Edit:
FYI, getSpectators has a "playerOnly" option because when a creature moves, it calls getSpectators with "playerOnly" as it doesn't need to notify other creatures or npcs of any data. Imagine a creature coming into view of a player who is standing still and the player did not have the data of that creature.

Check my signature out, trust me, I've been through this and made very efficient methods myself.
 
Last edited:

kay

Legendary OT User
Joined
Apr 23, 2013
Messages
943
Solutions
9
Reaction score
1,279
Location
32316,31942,7
YouTube
TibiantisOnline
You can't rely on getting initial data from a creature on them moving, because if you've just logged in and they are trapped in a corner, not moving, you won't get any data until they do.
Imagine a creature coming into view of a player who is standing still and the player did not have the data of that creature.
Your examples are beside the point. He doesn't ever use his getPlayers function to get info about creatures to send, but to get players that need to receive new data. When logging in, that player will get data of visible creatures from the map, there is no issue here. By the way, it's not just 15x11 tiles, but rather 18x14 on up to 8 floors (2016 tiles at worst).
Also, he said that everytime a creature moves, getPlayers is called to notify players. No issue with a new creature coming into a view of a player either, whether he stands still is irrelevant.
The actual issue here is that when the player moves, appears, or whatever, he doesn't notify non player creatures (only players) to trigger them and instead he has them all active all the time. So yes, monsters on the whole map move around without players (as I understand). That's why the OP chose to iterate players, cause he apparently doesn't care about notifying other creatures about anything. He just loops through all of them every round. Plus he assumed way fewer players online than tiles to search.
 
Last edited:
OP
OP
Source

Source

Well-Known Member
Joined
May 31, 2020
Messages
97
Reaction score
93
Location
OpenTibia .dev
GitHub
source61
So yes, monsters on the whole map move around without players (as I understand). That's why the OP chose to iterate players, cause he apparently doesn't care about notifying other creatures about anything. He just loops through all of them every round. Plus he assumed way fewer players online than tiles to search.
Yeah, I've kinda been through all of this earlier.
I haven't gotten to the point where notifying creatures has become an interest to implement.
Running over benchmarks of vectors and things here I must admit I was actually wrong, vectors are fast, virtually as fast as a simple for(i=0; i<n; i++) loop, but both are not as cheap as I thought, to my surprise they're both in the 0.5-1µs per iteration range.
This does mean that the option of iterating over all the creatures won't be as cheap as I thought.
It will still be doable to iterate over all players + creatures once per second, even with thousands of players, but not much more than that.
Meaning that iterating "over the tiles" will actually be cheaper, something I will act upon in the coming days/hours.

Edit: Nvm, I'm tripping balls, it's in the 0.001µs per iteration range. I'm used to using 1k loops, switched to 1m in case of inaccuracies using too low numbers, and miscalculated the costs. So yeah, iterating over creatures should still be fairly inexpensive, but you're still right that iterating over tiles will be cheaper, part of my map implementation got me worried that "accessing tiles" would be more expensive, but I did a benchmark and it's actually dirt cheap as it should be, so I'll still be switching over to a getCreatures(...) function over the idea of iterating over all creatures in the future.

Not in your approach.
Nah for sure, I meant using getSpectators which I don't have or use.

But you could notify all creatures there, where for monsters and NPCs it would mean changing their state (waking up) and then chaining them, so that later you don't have to iterate over and call all non player creatures to do actions (move, etc.) every round, like I think you do now(?). You said that you could easily add another loop through monsters list aside of your getPlayers function for that, but then it's better to search through the map for creatures than iterate all creatures and calculate the distance.
Speaking of Cip engine, you may want to take a look at their TFindCreatures class and ProcessCreatures function.
Hmm, I don't think that's feasible given that any new creature appearing would require a new round of chaining. But I guess you mean in terms of spells.
I don't iterate over creatures currently, I basically run an addEvent for each creature that recursively walks and sleeps in a while loop.
You're right about iterating over tiles is faster than iterating over creatures, I wasn't aware of how expensive any iteration or loop is to begin with and was under the impression that loops are cheap and why I believed iterating over creatures would actually be cheaper.
I'll make sure to do that. Appreciate it a lot.
 
Last edited:

amatria

Active Member
Joined
May 15, 2021
Messages
49
Solutions
4
Reaction score
45
Location
Spain
GitHub
amatria
I see. So if you were given the job to do one specific coding task, say write a function that accomplishes something, and you had the option to write either 5000 lines of code to accomplish the task or 1 you would choose the 5000 lines?
Your answer leaves a lot of details to imagination, but I will try to make a point.

First of, if your ultimate goal is to learn, you should go with the 5000 lines of code indeed. If the main objective behind this project is to learn and have fun, why would you avoid implementing the hard functionality yourself and, instead, rely on a library call? (That's what I assume from your response) [1].

Also, you imply that less code equals better, more performant and readable code. I can't help but disagree.

Look at the following python examples:
Python:
def for_loop_reduction(x, threshold):
    ret = 0
    for value in x:
        if value < threshold:
            ret = ret + value
    return ret

def list_comprehension_reduction(x, threshold):
    return sum([value < threshold and value or 0 for value in x])
Which of the above examples is more readable? Which one is more performant? Why? Is it true that less code equals more readable and faster code? (I leave as an exercise the answer to these questions).

Lastly, it looks to me that you take questions as harsh criticism. People are curious about your design decisions, and I am curious myself too. There are no obscure intentions in the questions above. We are all just trying to learn from your programming journey.

As for the thread's topic, I wish you the best of luck. Your progress so far looks very very promising :). By the way, I'll be glad to see further benchmarking, as I am a bit of a nerd in these sort of topics.

===
[1] It appears that you are confusing performance with productivity too.
 
Last edited:
Top