ID:2049761
 
So what kind of useful coding tricks have you acquired? I recently learned how to use the findtext feature as a quick search for summoning items out of a big list what about you?
1. I use a global update loop when dealing with pixel movement or projectiles.

2. I define variables outside of loops.

3. I keep track of huds using associative lists.

4. I try and avoid proc overhead by only making procedures for things that are called less than often.

5. I use only objects when defining things for the map as it makes my DMM loader work as I believe turfs are shit.

6. I keep a global list of players in almost every project, because it'll be of use eventually in any multiplayer project.

7. I avoid the webclient as it kills my motivation to see my project not work.
I use only objects when defining things for the map as it makes my DMM loader work as I believe turfs are shit.

This is a terrible idea, BTW. I've seen this ideology completely kill projects. This is why EPIC: Legend became unstable.
In response to Ter13
Ter13 wrote:
I use only objects when defining things for the map as it makes my DMM loader work as I believe turfs are shit.

This is a terrible idea, BTW. I've seen this ideology completely kill projects. This is why EPIC: Legend became unstable.

For those who don't know how to utilize DM, sure.
You do understand the overhead that objects on the map create?

Every time an object moves, it has to search through all turfs and movables it would encounter during that movement, check to see if the movement would actually encroach the object, call several functions to determine if encroachment is allowed, and then call several callbacks to notify it of the successful or failed encroachment, right?

Quite literally, if everything in your project is an object because you can't be bothered to use turfs, you are setting yourself up for failure the minute you make anything that exceeds a few thousand tiles in size.

http://www.byond.com/forum/?post=1999512#comment17777338
In response to Ter13
Ter13 wrote:
Quite literally, if everything in your project is an object because you can't be bothered to use turfs, you are setting yourself up for failure the minute you make anything that exceeds a few thousand tiles in size.

I have had my uses for it in the past and have yet to run into any negative impacts that could of came out of doing so.

Like I stated in my previous post "For those who don't know how to utilize DM, sure."
have yet to run into any negative impacts that could of came out of doing so.

That's because you've abandoned every project before it became significant enough to be a problem.

But hey, what do I know? I'm just one of the guys that apparently doesn't know how to utilize DM.
In response to Ter13
Ter13 wrote:
That's because you've abandoned every project before

And that will change.

But hey, what do I know? I'm just one of the guys that apparently doesn't know how to utilize DM.

I'm not saying you don't, I agree; users shouldn't do such if they can't utilize it for their purpose(s) efficiently.

Alright, sorry to pull this one off track:

A few tricks that I use:

Preprocessor macros:

Pseudofunctions:

#define floor(x) round(x)
#define ceil(x) (-round(-(x)))


They are like defining a proc, but they don't incur proc call overhead. Only use these where the input parameters are expected to be constants or single-access.


project-wide settings:

#define FPS 40
#define TICK_LAG 0.25
#define TILE_WIDTH 32
#define TILE_HEIGHT 32

world
fps = FPS
icon_size = TILE_WIDTH
...
...
...
sleep(TICK_LAG) //sleep for one frame
sleep(16*TICK_LAG) //sleep for 16 frames
sleep(10) //sleep for 1 second


Using these settings will allow you to change the desired FPS or tile size of the project on the fly without having to update hundreds of lines of code.
</DM>


Pseudointerface casting

interactable
proc
canInteract()
Interact()


obj
proc
canInteract(mob/m)
return 1

Interact(mob/m)
//do stuff

turf
proc
canInteract(mob/m)
return 1

Interact(mob/m)
return 1


And when you want to interact with something:

var/interactable/i = someobject
if(i.canInteract(src)) i.Interact(src)


This allows redefinition of hooks without worrying about polymorphic hierarchy.


Pre-initialization:

You can do stuff before the map is created.

var/init_something/__init_something = new() //global variable

init_something
New()
//do stuff here

world
New()
..()
__init_something = null



Singleton initialization:

var
list/skills
skill_manager/skill_manager = new()

skill
var
id
is_singleton = 1
proc
Use(mob/m)

skill_manager
New()
skills = list()
var/list/types = typesof(/skill) - /skill
var/skill/s
for(var/v in types)
s = v
if(initial(s.is_singleton))
s = new v()
skills[s.id] = s


This will let you globally populate a list of skills or something that can be accessed by unique id. They behave as singleton datums.

This serves many purposes. Let's say we want to remove a skill from the game? Rather than giving the player a skill object in their variables, we just create a list of skills that they know based on the skill's id.

When we load the player from a savefile, we check each of the ids in their known skills list and discard any that are no longer in the global skills list.

This will keep savefiles from crashing and having to be wiped when you remove a skill object that's in the player's savefile. There are tons of uses for this.


There are probably hundreds of little tricks/tips I've accumulated over the years. Couldn't possibly go in-depth with all of them.
In response to Ter13
Oh wow that last one is really helpful
Avoid proc calls like the plague.

The more proc calls you have, the slower your code is. This doesn't excuse shitty spaghetti copy/paste jobs, but usually, just copy/pasting your procs' logic into the scope of that intense loop you're using them in is several magnitudes faster than calling procs.

Neater, elegant, OOP-compliant code isn't always the fastest.
In response to Doohl
Doohl wrote:
Avoid proc calls like the plague.

The more proc calls you have, the slower your code is. This doesn't excuse shitty spaghetti copy/paste jobs, but usually, just copy/pasting your procs' logic into the scope of that intense loop you're using them in is several magnitudes faster than calling procs.

Neater, elegant, OOP-compliant code isn't always the fastest.

^ This, however I'm pretty good at cooking up some spaghetti ;)

But I'd disagree if the proc isn't called very often.
Don't obessively comment every line, but comment liberally. Explain to yourself what a proc does if it isn't super, super obvious. Even for things like DeathCheck(), at least explain what the arguments are--if not via a comment then by naming the arguments appropriately.

Use local vars as much as you can. If you're going to be accessing mob.level multiple times in a proc, for instance, but you may only need to read the var once, store it in a local var. Reading from a local var is super fast compared to reading from a datum.

If a proc's return value isn't important, . is a free var you can use. Because of its form, it won't play nice with lookup operators like :, but it can handle numbers and text and such very easily. (You can also use usr for this purpose, as a free local var, with the understanding that it's typed as /mob. However, this is not recommended because it can make your code confusing.)

If you know you're going to want to know whether a list contains a certain unique item, consider making that list associative. Setting list[item]=1 means that you only need to do if(list[item]) to see if the item is in the list.

Don't initialize lists before you have to. If you give a mob a var such as var/mylist[2], you're probably doing it wrong. Make it var/list/mylist and create the list once it's needed; never before.

Do as much as possible with atom.color rather than icon math. Your resources will be much more streamlined and the project will run a lot faster for it.

If you have to do string manipulation, use procs that will do it all under the hood such as jointext(), and when you can't do that, use things like "[a][b][c]" that build the string all at once. The fewer temporary strings you need to create, the better.

Create a file like !defines.dm and put all your important #defines in there. It will be compiled first. If you're going to use a lot of custom layers for atoms, define them all here.

Use #ifdef and #ifndef to let yourself turn behavior on and off at compile-time. This is also helpful if you're refactoring, because you can turn off old code but reinstate it if you need to.

For anything that might possibly be configurable, do yourself a favor and setup a system for loading and saving config files. The new JSON procs will help, but you can also do an .ini style like having "name = value" on each line.
In response to Ter13
Ter13 wrote:
Preprocessor macros:

Pseudofunctions:

#define floor(x) round(x)
#define ceil(x) (-round(-(x)))

They are like defining a proc, but they don't incur proc call overhead. Only use these where the input parameters are expected to be constants or single-access.

While I hate the idea of calling these "pseudofunctions" due to my math background (I'd just call them macros), the fact that there is no overhead can be very important for some projects. For my BigInt library, I had a method called InBounds() which basically verified that an index passed to a method was actually within the bounds of the array that stores the integer data, and it had a huge overhead because it was called so much, even though it was a fairly innocuous function. While it's good OOP to make this it's own method, in practice it was far better on my execution times to throw it inside a macro instead. Just by doing this one thing, I saw immense speed gains in my code.

As for my own contribution:

Having at least some idea of what's going on under the hood, whether it's DM's interpreter, the operating system, the logic behind the algorithm you're using, or how computers even work, is extraordinarily useful. Sometimes, it's easy enough to just take the parts and put them into place, and nothing bad will happen and everything will work great; but sometimes, you have to actually know what the parts do or everything will break.
Yes these should be #defines.

No I don't give a fuck.

This is my Rushnut STD.dm, my book of tricks, I suppose.

As for an overarching theme, . is faster than return. Fun tip.

proc
atan2(x, y)
if(!(x || y)). = 0
else . = x >= 0 ? arccos(y / sqrt(x * x + y * y)) : 360 - arccos(y / sqrt(x * x + y * y))

angle2dir(angle)
switch(clampAngle(angle))
if(0 to 22.5) . = NORTH
if(22.5 to 67.5) . = NORTHEAST
if(67.5 to 112.5) . = EAST
if(112.5 to 157.5) . = SOUTHEAST
if(157.5 to 202.5) . = SOUTH
if(202.5 to 247.5) . = SOUTHWEST
if(247.5 to 292.5) . = WEST
if(292.5 to 337.5) . = NORTHWEST
if(337.5 to 360) . = NORTH

dir2angle(dir)
. = 0
switch(dir)
if(NORTHEAST) . = 45
if(EAST) . = 90
if(SOUTHEAST) . = 135
if(SOUTH) . = 180
if(SOUTHWEST) . = 225
if(WEST) . = 270
if(NORTHWEST) . = 315

clampAngle(angle)
. = angle % 360 + angle - round(angle)
if(. >= 360) . -= 360
if(. < 0) . += 360

screenLocToPx(var/S)
var/list/screenLoc = parseScreenLoc(S)
. = list((screenLoc[1]*TILE_WIDTH)+screenLoc[2],(screenLoc[3]*TILE_HEIGHT)+screenLoc[4])

parseScreenLoc(var/S)
if(!isnum(text2num(S)))
S = copytext(S, findtext(S, ":")+1)
var/x = text2num(S)
S = copytext(S, length("[x]")+2)
var/px = text2num(S)
S = copytext(S, length("[px]")+2)
var/y = text2num(S)
S = copytext(S, length("[y]")+2)
var/py = text2num(S)
. = list(x,px,y,py)

moveScreenLoc(var/screenLoc,var/x,var/px,var/y,var/py)
var/list/screenLocs = parseScreenLoc(screenLoc)
screenLocs[1] += x
screenLocs[2] += px
screenLocs[3] += y
screenLocs[4] += py
. = "[screenLocs[1]]:[screenLocs[2]],[screenLocs[3]]:[screenLocs[4]]"

parseDimensions(var/dimension,var/seperator = "x")
var/x = text2num(dimension)
var/y = text2num(copytext(dimension,length("[x]")+2))
. = list(x,y)

sign(n)
. = ((n) && ((n) / abs(n)))

floor(var/n)
. = round(n)

ceil(var/n)
. = -round(-n)

cx(var/atom/M)
. = istype(M,/atom/movable) ? floor(px(M)+(M:bound_width/2)) : floor(px(M)+(TILE_WIDTH/2))

cy(var/atom/M)
. = istype(M,/atom/movable) ? floor(py(M)+(M:bound_height/2)) : floor(py(M)+(TILE_HEIGHT/2))

px(var/atom/M)
. = istype(M,/atom/movable) ? ((M.x-1)*TILE_WIDTH)+M:step_x+M:bound_x+1 : ((M.x-1)*TILE_WIDTH)+1

py(var/atom/M)
. = istype(M,/atom/movable) ? ((M.y-1)*TILE_HEIGHT)+M:step_y+M:bound_y+1 : ((M.y-1)*TILE_HEIGHT)+1

dx(var/atom/A,var/atom/B)
return (cx(A)-cx(B))

dy(var/atom/A,var/atom/B)
return (cy(A)-cy(B))

getRefs(var/datum/D) //Semi broken, very slow, for debugging only.
. = new/list()
var/count = 0
var/list/searched = new/list()
for(var/datum/D2)
searched += D2
for(var/v in D2.vars)
if("[v]" == "vars")continue
if(D2.vars[v] == D)
count ++
.["[count]: [D2]"] = "[v]"
else if(istype(D2.vars[v],/list))
for(var/v2 in D2.vars[v])
if(v2 == D || D2.vars[v][v2] == D)
count ++
.["[count]: [D2]"] = "[v]"
for(var/datum/D2 in world)
if(D2 in searched)continue
searched += D2
for(var/v in D2.vars)
if("[v]" == "vars")continue
if(D2.vars[v] == D)
count ++
.["[count]: [D2]"] = "[v]"
else if(istype(D2.vars[v],/list))
var/list/L = D2.vars[v]
for(var/v2 in L)
if(v2 == D)
count ++
.["[count]: [D2]"] = "[v]"

clients(var/atom/movable/ref,var/dist = 0)
. = new/list()
for(var/client/C)
if(C.mob)
if(dist)
if(get_dist(ref,C.mob) > dist)continue
. |= C
As for an overarching theme, . is faster than return. Fun tip.

Not true in my experience. More convenient in some cases, yes, but faster, no.
I sacrifice goats to Dan for good code crops.
Not true in my experience. More convenient in some cases, yes, but faster, no.

Setting the return value can in some cases be faster than directly invoking return. In other cases it can be slower, though these cases have nothing to do with the return value. It has to do with scope traversal and internal housekeeping DM has to do.

Generally, the biggest things I've found with respect to slow code:

1) Redundant instructions.

2) Invocation overhead.

3) Excessive scope traversals.

4) Out-of-scope variable definition.

...

...

...

12376) . vs return

12377) |= vs +=

Yut Put wrote:
this is so counter intuitive holy shit

everything i knew was a lie

DM's compiler is a real piece of garbage in this respect. It abstracts proc calls when they should be inlined or handled via jumpcodes at compile time. Unfortunately, strict method invocation isn't possible in DM because the language is user-friendly.

In many languages, if you do something like:

var/obj/sometype/s = new()
s.Herp() //will call obj/sometype/Herp()

You'll call a totally different function than:

var/obj/s = new/obj/sometype()
s.Herp() //will call obj/Herp()

DM solves this issue by delegating the call to the instance in the cast variable, while other languages allow you to neglect polymorphic overrides and call lower-level functions based on the structure of the class that the instance is cast as.

The real trouble is that the compiler has no way of knowing which function will be invoked at compile time because EVERY pointer (even src) is indeterminate in terms of class structure.

I'm sure compiler overhead could be solved to some satisfaction (and I've got a good bit of hobbyist experience writing VMs), but tackling the task would be at such a deep part of the BYOND's guts that there's a good chance of a breaking change (see the most recent Lummox status update for evidence of unintended consequences arising from better implementations.).

It's really about measured advantages. You really want to manually inline some bits of your code (preprocessor macros can help a bit here, but it can be fairly counterintuitive), and taking advantage of polymorphic variation becomes the sole focus of incurring that invocation overhead.
In response to Nadrew
Nadrew wrote:
I sacrifice goats to Dan for good code crops.

how come your codes arent good then
I allways try minimize place in code.
//This

mob/proc/someproc()
if(something)
world<<something

mob/proc/someproc()
if(!something)
return
if(!something2)
return
if(!something3)
return
...
//i replaces by this

mob/proc/someproc()
if(something) world<<something

mob/proc/someproc()
if(!something || !something2 || !something3) return
...
Page: 1 2 3 4 5 6