ID:2151126
 
Not a bug
BYOND Version:510
Operating System:Windows 10 Home
Web Browser:Microsoft Edge
Applies to:Dream Maker
Status: Not a bug

This is not a bug. It may be an incorrect use of syntax or a limitation in the software. For further discussion on the matter, please consult the BYOND forums.
Descriptive Problem Summary:
http://pastebin.com/Jevwc8UK

Move called 3041959 times in just a few seconds. Spent almost half a day investigating a login delay. Turns out it was BYONDs default login feature that Move()s player to near locate(1,1,1) if their location is null.

I think 3041959 is far too many calls for a seemingly simple feature. So my personal problem has been fixed but, the problem was very hard to track and it caused horrendous server lag everytime someone logged in simply because I set their loc equal to null. I think this feature should be modified to your liking so its not able to produce server lag.

I would like to note that locate(1,1,1) was surrounded by water which is one of the reasons Move() was called so many times by the default login.
I think this feature should be modified to your liking so its not able to produce server lag.

Simply not including supercall in Login() or setting loc prior to calling the default supercall will disable the feature per documentation.

Can you elaborate on what you mean customize to your liking?
When the map is large and the bottom is full of either dense movables or non-dense turfs whose Enter() returns FALSE, the default behavior of mob.Login() will call mob.Move() on every tile, scanning rightwards from (1, 1, 1) and wrapping around to y+1.

This can cause huge CPU usage if you don't know how to avoid it: by setting loc to something other than null before calling Login()'s default behavior, or just not calling ..() in mob/Login().

How might this default behavior be more easily avoided? One idea is to give Login() a parameter like "PlaceNearOrigin = TRUE" that we can set to FALSE:
// e.g. 1
mob/Login(PlaceNearOrigin = FALSE)
..()

// e.g. 2
mob/Login()
..(PlaceNearOrigin = FALSE)
I understand that now Ter13, and I have fixed my problem. But I don't think a default byond feature should have the capability of calling the Move function 3 million times. Theres no plausible reason from my point of view to give a default function that much Power, not to mention its a feature not very known/understood by the community. I've been developing on byond well over 10 years and had no idea it had this capability of lagging my game to death everytime someone logs in.

And what I mean by customize to your liking... I was gonna suggest setting a limit to how many times it can call Move() but Lummox being the great developer that he is might have a more sound approach.

The main reason I'm reporting/requesting this is because the problem I encountered was extremely hard to track. I literally removed all my client/New(), mob/Login(), and nearly broke my source trying to figure out what code I did was causing this insane lag spike. Not to mention the lag occurred the moment a user connected to the world. And occurred before my debugging output in client/New() and mob/Login(). This problem was very difficult to track and I just want to prevent others from going through what I went through.
This is not a bug, the fact that it calls so many times is purely a result of your map design and lack of understanding of how it works.
Nadrew resolved issue (Not a bug)
*Accepts fact default login feature is capable of calling Move function 3 million times within a second.*
It'll only do that if you allow it to, the issue is the sheer size of your map and the fact that you didn't know what "..()" inside of Login() actually does. There's definitely nothing going on that isn't documented and intended.
Is there anything else that happens besides this when I use ..() inside login?
Thx Ter13. I actually was reviewing the first link earlier today. Its clarified why the default login lag would happen so early when someone was connecting. It was because "mob/Login() is called before client/New() returns". My next question probably shows my lack of understand but I had written this code.
client
New()
world<<"[src] has connected"
..()

Logically, that world output would be displayed then the default functioning continue. But that's not the case.
So I presume when connecting, the default client New() runs
calls mob Login() then allows any other client/New() functions to run which is why all my debugging output was displayed after the lag spike.
Haha is that why this is?

I was wondering one time why when I made a dummy mob to move a player into before moving him into his real one (which was initialized in nullspace) there was this HUGE lagspike!

Such bizarre behaviors byond has sometimes!
calls mob Login() then allows any other client/New() functions to run which is why all my debugging output was displayed after the lag spike.

That's actually not the case. We can test this theory.

client
New()
world.mob = /mob/test
..()

mob
Login()
world.log << type
test
Login()
world.log << type


If the client/New() is called after the default action, we'd expect that the client would be given a mob of type /mob, and not /mob/test, as is what happens.

The code flow happens in the appropriate order. Perhaps it is the display of the output itself that is causing the delays. Have you considered testing whether world << "herpderp" is consuming lots of CPU because of the sheer number of objects in your world?

EDIT: Tested it. It's not. A swarm of world outputs are processed identically regardless of the number of objects in the world. It seems that it only loops through the clients in the world, and doesn't involve the world.contents at all.
Well. I'm positive world<< wasn't causing the lag delay. I'll run some test tomorrow within these circumstances to validate your above example. Again, I appreciate the clarification.
Honestly, the login() behavior is odd, we attach something that should be happening in client/New() and/or mob/New() to a totally unrelated proc, mob/Login()

A user logging into your server is client/New(), not mob/Login, but I've had to explain this to new programmers at /tg/ a few times.

mob/Login() is when a client is assigned a mob, be it at the start of their connection OR mid way into their game, the name is bad, and the behavior of the default is bad.

This will never change, sadly (too many games rely on it), and is indicative of a larger paradigm idiosyncrasy, that a lot of things that revolve around clients use mobs in byond, usr (something that should point to a client, not a mob) being a glaring example.
@MSO: The client/mob distinction is a difficult one to make.

It's hard for many programmers to determine what behavior belongs to the client, and what belongs to the mob.

I've (mostly) addressed this problem with some of the code I've socked away on my hard drive, and even some that I've shared.

SS13 has always been unique in the regard that very little behavior is NOT abstracted in some way behind a generalized implementation, which is pretty cool.

To expand on your overall point, though, there are some glaring mistakes in BYOND's implementation that inherently train new programmers to think backward:

1) usr... Exists and its declared type is completely immutable in the compiler's eyes. (We should be able to retarget what the compiler THINKS is the declared type of usr via #DEFINE __USR_TYPE__ at any specific region of the code that is currently being compiled. The distinction between access (.) and runtime access (:) is only for convenience, as, I believe the end bytecode is the same. Runtime accesses simply suppress most compiler errors.

2) client/Mouse functions do not pass through mob Mouse functions. mob should have MouseDown()/MouseUp() passthroughs. Unfortunately, this conflicts with atom/MouseDown(), which should have actually been named atom/onMouseDown() or something like that to keep the naming convention consistent, where MouseDown() is when it the mouse event actually fires, and onMouseDown() is the event where the mouse event has notified the concerned object that the event has happened.

http://www.byond.com/forum/?post=2145482

3) client/New() happens AFTER the client has connected, and client/Del() happens BEFORE the client has disconnected. mob/Login() happens AFTER the client has connected, and mob/Logout() happens AFTER the client has logged out. This really fucks most people out because one of these events is out of sync.

We already have passthroughs for almost everything else the client actually does:

Move(), Stat(), Topic(), Del(), New() all more or less have notification passthroughs to mob or statobj.

It's just weird to me that the Mouse passthroughs go directly to the objects interacted with, and aren't validated by the mob by default.
You see, you are still thinking of mobs as something inherently user control. fatal error.

The proper way to think of movables and clients is:

client, the user/player.

obj, a movable in the realm without agency. It doesn't act on it's own, it only reacts to the actions of things with agency.

mob, a movable in the realm with agency, it primarily acts of it's own accord, either via actions by a client or AI code.

Click should not pass thru a mob because a mob does not click on things, the client does. Clicking is an Out Of Character action, Mobs are in In Character concept.

In ss13, we generally have 4 times as many mobs and maybe 5 times as many mob prototypes that aren't player controlled vs ones that are player controlled.

We also see login the way we do because the avg client will be assigned to 5 mobs in the course of a round. Login/logout is the act of (un)assigning a client to a mob, using it for anything relating to client (dis)connection is a misnomer.

(I should point out that ss13 overrides all of byond's click handling and passes it thru mobs via the attack or use procs)

(we also do the same with move(), and handle processing bump/cross/enter/moved/entered/crossed/uncross/etc on our own so we can decide when to keep passing bumps thru to things and other flexibilities)
client/New() happens AFTER the client has connected, and client/Del() happens BEFORE the client has disconnected.

This has to be the case, it just has to.

It's the same with datum New/Del, New() runs after the datum is created, Del() runs before the datum is destroyed

Why would it work any other way?

If you want something to happen before clients connect, you want to override world/Isbanned()
You see, you are still thinking of mobs as something inherently user control. fatal error.

Eh, I kind of disagree with this. The mob serves as the eyes, ears, and hands of the client. The client knows what it clicked on, but it doesn't really know anything about what effect the click will have in the world.

That's where the mob comes in. The mob serves to translate the client's actions into reality.

Anything that passes through an input function from the client should be translated from the mob into an in-character action.

I don't think of mobs as inherently having user control, but rather, having the capability of user control.

I think of an AI as a simulated client, and as such, the AI should interact with the mob in a similar, but of course, not the same way as a client does. The client calls out to input handlers on the mob, the AI serves AS the input handler for the mob.

I thought like you a few years back, with the idea that anything involving an input should be on the client, but I realized that by building /controller datums to serve as modal controls for different kinds of mobs, I was creating a lot more work than necessary.

In the end, I found that the very best way to provide different controls for different mob subtypes was to simply abstract the control to the mob itself.

This communicates itself clearly and doesn't require passing data back and forth between two objects, or one object having deep information about the prototype properties of what it is controlling.

It's the same with datum New/Del, New() runs after the datum is created, Del() runs before the datum is destroyed

Why would it work any other way?

No, you misunderstand. I like how New/Del works. I don't like how Login/Logout works, because it lacks parity. I want Logout() to occur BEFORE the client jumps to another mob or is destroyed during a disconnect. I understand why it is the case that it doesn't, but I think it is a problem that there is a solution for. The event that is out of sync is Logout().
Page: 1 2