ID:116374
 
Applies to:DM Language
Status: Open

Issue hasn't been assigned a status value.
Turfs in BYOND are special objects, such that they can't be deleted without being replaced by a new turf.

What I'm suggesting is making it so we can specify certain variables that retain their value when a turf is replaced. (So that the value of the variable isn't set back to its initial value in the New proc.)

Some built-in vars already function this way. i.e. contents and loc.

It's easy to see how useful this could be. For example, if there is a level of light shining onto a turf, when it changes to a new turf, the amount of light shining on it wouldn't change, so the variable for light should remain the same. I'm sure there are many uses for this feature.

It could be implemented as a simple type specifier such as how tmp, const, and static currently function. The keyword 'dynamic' could be used. (Similar to the 'dynamic' keyword in C++ and similar in usage.)

So, the following code would be how to use this feature:
turf
var
tmp
tmp_var
dynamic
dyn_var1 = 0 //optional default value set when world is created
dyn_var2
dyn_var3


Of course, there are currently some ways to do this, although, all of them cause the code to either become needlessly inflated or cause other issues that make them unfeasible.

Method 1:
turf
New(L, some_var, another_var, random_var, too_many_vars) //easily inflated
//can't forget to always pass every parameter in new
//can't have parameters for other purposes
..()
dyn_var1 = some_var
dyn_var2 = another_var
...

This method causes obvious problems. It inflates the new proc, because it needs another parameter for every new variable. And since those parameters are taken, you can't use them for other purposes. Also, you have to remember every time you use new to make a turf to pass the turf's own variables. Many obvious problems.

Method 2:
turf
Del() //also easily inflated
var/temp1 = dyn_var1
var/temp2 = dyn_var2
var/temp3 = dyn_var3
spawn() //spawn causes write-protection issues
dyn_var1 = temp1
dyn_var2 = temp2
dyn_var3 = temp3
..()

This causes the code to become inflated as well, since you need an additional 2 lines for every variable. This is better than the first method, since you can still use New() without taking up countless parameters. And since the assignments can't take place until the next New() proc is called, they have to be spawned so they occur after. Spawning is not what we want here, since the variables should be set before the New() proc is called.

Method 3:
For every turf, it could have an invisible object on it that contains all of those variables that is created when the world is created. However, this causes the added overhead of needing to create objects in a loop for every turf in the world, which could cause a slow start-up. There's also the problem of having to use an extra level of indirection to access the values, not to mention you'd have to search the contents to find the object before you could access it.

There are other methods, I'm sure, but none of them are useful for maintaining a solid code base.
When you create a turf at runtime, won't its constructor always be passed the turf it's replacing? Assuming this is the case and there's a way to have references to both the new turf and the one its replacing, this should be easier than your examples suggest:

var
list/dynamic_vars = list("light")
turf
var/light = 0
proc/copy(turf/t)
if(!t) return
for(var/v in dynamic_vars)
vars[v] = t.vars[v]
New(turf/t)
copy(t)
..()


The only nuisance is that you have to call copy in the constructor. If you override a turf's constructor and deliberately don't call ..(), you need to remember to call copy(). You also have to maintain the dynamic_vars list, but either way you have to indicate which vars are dynamic.
No, the first parameter of New() is equal to src in that case, so that wouldn't be a reference to the previous turf. (The previous turf is completely gone and its Del has been called by the time New is called.)

I just tested to see if it would work.

Also, since turfs work a special way compared to other objects, it actually is a reference to the past turf, since the internal pointers don't actually change. If you used \ref[src] in the Del and New procs, you'd see that it returns the same value both times.

(So, in effect, the concept of both turfs existing simultaneously is impossible.)
Even if you can't do it in the constructor, you can use something like the copy proc I wrote that uses the vars list to handle it. It looks like the destructor is the place to do this. In some ways this might be better, its more likely that you'll always call ..() in Del(), so if you implement this yourself it'll always be executed. You just have to maintain the global list of dynamic vars.
However, it doesn't fix the problem with having to use spawn() and thus not having the values copy over until after New() is already done.
Though, thanks for showing me a way to not make the code look as ugly. (By ugly I mean prone to human error.)
Can you write the values to a global var in Del, then read them from the global var in New?

var
list/dynamic_vars = list()
list/scratch_space = list()
turf
Del()
for(var/v in dynamic_vars)
scratch_space[v] = vars[v]
New()
for(var/v in dynamic_vars)
vars[v] = scratch_space[v]
..()
Say you used spawn to create multiple turfs at once. All of that would be executing at the same time, which can result in a race condition. I suppose you could create a critical section-like thing to prevent this, but that would cause a delay between turfs being created, which is not the best result. (Since you originally spawned them and expected them to all be created at once, and instead you see one being created every 10th of a second.)

But, indeed, it helps a little.
It just occurred to me that I could get this working if I used sleep(0). I don't know why I didn't think of that.
turf
var
static
list/dyn_vars = list("dyn_var")
vars_lock = 0
list/temp_vars = new
dyn_var
New(L)
if (vars_lock)
for (var/V in dyn_vars)
vars[V] = temp_vars[V]
vars_lock = 0
..()
Del()
while (vars_lock)
sleep(0)
vars_lock = 1
for (var/V in dyn_vars)
temp_vars[V] = vars[V]
..()

I think this works, so I guess this feature isn't going to be implemented. D:
Everything is still single-threaded. Even if you use spawn() to create the new turfs, I wouldn't think its possible to have anything happen between the calls to Del and New for the same turf.

I think this works, so I guess this feature isn't going to be implemented. D:

Most features won't be implemented. Whether there's a workaround or not, there are too many feature requests for them to keep up with. I figure its always a good idea to look for ways to create features yourself, just in case :-)
Don't forget you need to call ..() in Del().
Forum_account wrote:
Everything is still single-threaded. Even if you use spawn() to create the new turfs, I wouldn't think its possible to have anything happen between the calls to Del and New for the same turf.

I was pretty sure there are still virtual threads in the virtual machine that handle spawn calls by rapidly switching between them if they are of the same priority.
I can't imagine that's the case. It's still going to take just as long to finish all of the work for all of the spawn calls and the changes essentially take effect when the client receives the update message. There's no benefit to doing things in "parallel", it just creates the potential for race conditions.
Complex Robot wrote:
Forum_account wrote:
Everything is still single-threaded. Even if you use spawn() to create the new turfs, I wouldn't think its possible to have anything happen between the calls to Del and New for the same turf.
I was pretty sure there are still virtual threads in the virtual machine that handle spawn calls by rapidly switching between them if they are of the same priority.

BYOND is not multithreaded, so this is not the case.