Jump to content


Photo

Execs: Gml Threads And Asm In Gm| Update:19 Feb 09


  • Please log in to reply
29 replies to this topic

#1 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 02 February 2009 - 06:48 PM

Execs DLL
by Score_Under (Charles Daffern).
Last update 19th February 2009. 3 new functions, still supports 4 versions of Game Maker.
The "asm.dll" included with this package is a version of the assembler included with OllyDBG made to be compatible with GM and Execs.dll, and is therefore property of Oleh Yuschuk.
The "cc3250.dll" is simply a runtime from Borland to get "asm.dll" to actually work.
"Execs.dll" is mine, of course! :(


OllyDBG is also useful to debug any mistakes you make with the machine-code execution functions of this DLL.
Compatible with: :D (5.3 only) :P (GM6.1 and GM6.0) :D (7.0 only)

This DLL contains many functions, including:
  • Threads
  • execute_string, for all above versions, also accessible from your own DLL (like an SDK).
  • Conversion to hex (for ASM)
  • Assembler
  • Memory read/write/allocate/free
  • Execute machine code in memory

First, some warnings.

Threads
DO NOT exit the process before the threads have finished. This will cause an access violation.
DO NOT terminate the threads with thread_end unless you really have no other choice.
DO NOT access local variables from the thread. It will crash the game.
Workaround: use "object0.x = 5" or "with(object0)x=5".
If your code is in a loop, ALWAYS put code into your thread to exit, for example, "if (global.exiting) exit;", to close the thread safely when you want to.

Memory / Execution
When executing a function - check what it returns. If it returns a double, then you will have to pop the FPU stack before returning your own value. If it doesn't return a double, you *must* add a command like FLDZ before the return.
NEVER overwrite more memory than you've allocated. ALWAYS allocate more than you need.
NEVER "lose" a pointer to still-allocated memory before freeing it.
NEVER free memory twice.
If your game crashes inside mem_exec, chances are it's your fault and not the DLL's.


ASM
Always remember to set_ptr!

Now, for some info.
If you do not wish to use any ASM functions, you can delete ASM.dll and cc3250.dll.
Info

Threads
thread_init() - Determines the version of Game Maker, and gets the correct handle to execute_string().
thread_exec(string,suspended) - Starts a new thread, executing the code in the string. Set argument2 to SUSPENDED to start suspended, or NORMAL to start straight away. Returns a thread handle.
thread_priority(handle,priority) - Resets the priority of the thread.
thread_suspend(handle,suspend) - Suspends or resumes the thread (set the second argument to either SUSPEND or RESUME)
thread_exists(handle) - Checks if a thread exists.
thread_affinity(handle,mask) - Sets processor affinity mask for a thread. "3" is processors 0 and 1.
thread_affinity(handle,processor) - Sets the ideal processor for a thread.
thread_count() - Returns the number of threads left. Slow.
thread_end(handle) - Abruptly terminates a thread. ONLY USE IF YOU CANNOT TERMINATE IT BY ANY OTHER MEANS.
unload_execs() - What you get if you apply thread_end() to all threads.
get_processors() - Returns the number of processors on the system.
is_slow() - Returns true if the current operating system believes that your computer is slow.

Memory
These functions were made to be used in conjunction with ASM functions.
It is possible you could assemble the code beforehand, however.

get_module_handle(name) - Returns the module handle for a module (e.g. get_module_handle("user32"); )
get_proc_address(module handle, procedure) - Returns the address for a procedure inside the specified module handle. (e.g. get_proc_address(get_module_handle("user32"),"MessageBoxA"); )
get_game_module_handle() - Returns the module handle for the GM game. Usually $400000.
get_gm_proc_address(name) - Returns the procedure address for any Game Maker built-in function. New! 19th Feb 09
set_gm_proc_address(name,address) - Sets the procedure address for any Game Maker built-in function. Use with extreme care. New! 19th Feb 09
load_library(name) - Loads a DLL into memory, returns the module handle.
free_library(handle) - Frees a DLL from memory.
mem_exec(address) - Executes the machine code at the memory address specified. Use in conjunction with ASM functions.

All read_* functions return the value read.

read_int(address) - Reads a 4-byte signed integer from the specified address.
read_str(address) - Reads a whole NULL-terminated string from the specified address.
read_uint(address) - Reads a 4-byte unsigned integer from the specified address.
read_byte(address) - Reads a single byte from the specified address.
read_double(address) - Reads a double (GM's "real") from the specified address.

All write_* functions return 0.

write_int(address,int) - Writes a signed integer to the specified address.
write_string(address,str) - Writes a whole string to the specified address.
write_uint(address,int) - Writes an unsigned integer to the specified address.
write_byte(address,byte) - Writes a byte to the specified address.
write_double(address,double) - Writes a double (real) to the specified address.
write_bin(address,value,length) - Writes binary data (like a string, but can have chr(0) in it) to the specified address.
mem_set(address,byte,length) - Repeatedly writes the specified byte to the address.
mem_copy(dest_address,src_address,length) - Reads binary data from src_address and writes it to dest_address.
mem_alloc(length) - Allocates length bytes. Returns pointer.
mem_free(pointer) - Frees a pointer. Always use once you've finished with your memory. Do not use on random addresses.
virtual_protect(address,size,new_permissions) - Changes the protection of a memory page. Used to avoid crashes with some ASM code. New! 19th Feb 09

ASM
init_asm() - Initialize ASM.dll
asm(string) - Assembles the ASM string to the current pointer, then increments the pointer by the length of the assembled code.
set_ptr(pointer) - Sets the pointer used by ASM.dll.
get_ptr() - Returns the pointer from ASM.dll.
to_hex(num) - Converts a number to hex, and if the first hex char is a letter, prepends "0".

If you need disassembly functions, PM me.

Writing your own DLL with execute_string
You only need to include "execs.dll" with your game. Call "init_execs()" from Game Maker, then after init_execs has executed, in your DLL, execute code similar to this:
(Works in all GM versions mentioned at the top of the post, and as always, don't set local variables without using a with(obj) or a obj.var statement)
typedef double type_execute_string(char*str);

int call_gml(char*a)
{
  type_execute_string* exec;
  
  exec=(type_execute_string*)GetProcAddress(GetModuleHandle("execs.dll"),"execute_string");
  if(exec)
	exec(a);
  else
	return 0; //failure
  return 1; //success
}
Example of usage: DLL 3D model reader - you can just sprintf() the values onto a GML string, which can be executed as "with (object){vertex[0]=13.55; vertex[1]=43; ...}", thus meaning only 1 DLL call and much less overhead.

A final word:
This is not for the faint of heart. Especially not the ASM capabilities.

Download: >ZIP< or >RAR< for those of us who prefer a smaller filetype, even with the recovery record.
>ZIP Mirror<
Contains Execs.DLL, ASM.DLL, Borland C++ runtime DLL, 2x GMD, 2x GM6, 2x GMK.

You do not have to give credit. You may use this in commercial and non-commercial projects.

If you find any glitches, PM me right away, with the code that caused it.
Yes, I have been teaching X-Tra Fear. :(

Edited by score_under, 24 February 2009 - 08:35 PM.

  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#2 HaRRiKiRi

HaRRiKiRi

    GMC Member

  • GMC Member
  • 1364 posts

Posted 02 February 2009 - 08:27 PM

This is certainly for not faint of heart. :D But very nice job you have done. This creates lots of new possibility's.
  • 0

#3 uuf6429

uuf6429

    Covac Software

  • New Member
  • 2522 posts
  • Version:Unknown

Posted 03 February 2009 - 10:47 AM

And this is your very first dll?!

Nice one!

I might find some good use.
  • 0

#4 asmodeus

asmodeus

    Game Programmer

  • GMC Member
  • 338 posts
  • Version:GM8

Posted 03 February 2009 - 01:39 PM

Could be useful for precise variable handling. For example you can write at address xx a single byte which is a certain value. If you use game maker variables I never know how much memory they need. And if you don't need that variable (byte) any longer you could use it for other things. Am I right?
  • 0


#5 Clam

Clam

    GMC Member

  • New Member
  • 55 posts

Posted 03 February 2009 - 02:20 PM

I'm guessing you had to dip into asm for the thread_exec and execute_string functions. I've managed something similar with C & inline asm but it's still very prone to crashing (something as simple as "i=1" makes it try to access self and it crashes). If you've done it purely in C++ I'd be very interested to know more. Next step will be running script_execute in a similar way, and then...world domination!

Props for not just wrapping the assembler function, but actually writing working examples. One thing to watch out for is whether Borland allows you to redistribute their runtime.

asmodeus: A "real" variable takes up 8 bytes, and a string variable uses up 4 bytes + number of characters in the string + 1 null byte, although I have heard somewhere that 1024 bytes are allocated for each string to start with. Having said that, memory usually isn't an issue.
  • 0

#6 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 03 February 2009 - 08:49 PM

I'm guessing you had to dip into asm for the thread_exec and execute_string functions. I've managed something similar with C & inline asm but it's still very prone to crashing (something as simple as "i=1" makes it try to access self and it crashes). If you've done it purely in C++ I'd be very interested to know more. Next step will be running script_execute in a similar way, and then...world domination!

Props for not just wrapping the assembler function, but actually writing working examples. One thing to watch out for is whether Borland allows you to redistribute their runtime.

asmodeus: A "real" variable takes up 8 bytes, and a string variable uses up 4 bytes + number of characters in the string + 1 null byte, although I have heard somewhere that 1024 bytes are allocated for each string to start with. Having said that, memory usually isn't an issue.

I have used ASM long before that, mainly for reverse engineering (and now, for finding out the format of the execute_string function).

Currently, the only info I have on execute_string (only tried with 1 argument) is that it takes EAX and EDX as the pointer to the current object (no idea how to find the pointer, though) and ECX should be 1. The first argument is a pointer to a structure of 6 4-byte variables, which are all 0, and the second argument is 15. The third argument is a pointer to the same structure (as far as I know) as the first argument, but with the first variable set to 1 and the fifth variable set to the pointer to the string (except the string has to have its length declared in the 4 bytes preceding the first character... this is just standard Delphi string behaviour, it seems).

The only reason I included a wrapper to the assembler is because I really hate having to continually look up the commands, and then performing operations that most people would not understand ("Why do you subtract the location of the next instruction from the destination of the call?").

EDIT: This is pretty outdated now that I read it, I know a lot more about the formats.
I also know that if YoyoGames put the string part of a variable before the real part, they'd save 8 bytes per variable.

Edited by score_under, 03 June 2009 - 01:41 PM.

  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#7 uuf6429

uuf6429

    Covac Software

  • New Member
  • 2522 posts
  • Version:Unknown

Posted 04 February 2009 - 12:22 PM

score_under, I suggest you pay more attention to what you say. Reverse engineering ain't encouraged at all here.
Its nothing personally, I find your info very interesting and enlightening, but...
  • 0

#8 Clam

Clam

    GMC Member

  • New Member
  • 55 posts

Posted 04 February 2009 - 01:38 PM

No mods have complained yet, and this kind of minimal reverse engineering can really only benefit users. That kind of info is basically what GMThreads uses. The only real cause for concern is if people start writing dodgy dlls that are prone to crashing and make people think gm's engine is unstable. I somehow think anyone who understood that would do no such thing ^_^
  • 0

#9 uuf6429

uuf6429

    Covac Software

  • New Member
  • 2522 posts
  • Version:Unknown

Posted 04 February 2009 - 06:46 PM

What I'm saying is not to take mods lightly. To me, this doesn't make any difference at all.
  • 0

#10 paul23

paul23

    GMC Member

  • Global Moderators
  • 4147 posts
  • Version:GM:Studio

Posted 04 February 2009 - 07:12 PM

Hmm this seems very promising: so would I be able to "call" a very slow script which runs on a seperate core and hence doesn't slow down the game with this? (like: thread_exec("myterriblyslowscript,NORMAL) )

Also how does it work with global variables?
  • 0

#11 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 04 February 2009 - 07:23 PM

Hmm this seems very promising: so would I be able to "call" a very slow script which runs on a seperate core and hence doesn't slow down the game with this? (like: thread_exec("myterriblyslowscript,NORMAL) )

Also how does it work with global variables?

It works fine with global variables, but I don't know the structure for objects in Game Maker, so I can't allow it to work with locals.

These codes work:
with(object0){ a=0; x=30; b=a; y+=1; variable_local_set("a",0); }
global.a=2; global.c+=1;
object0.a=1; object0.b+=1;
variable_global_set("a",0);

These codes fail:
a=0; x=30; b=a; y+=1;
variable_local_set("a",0);

You could, as you said, call your very slow script on a different core.

Edited by score_under, 04 February 2009 - 07:24 PM.

  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#12 Yourself

Yourself

    The Ultimate Pronoun

  • GMC Elder
  • 7352 posts
  • Version:Unknown

Posted 04 February 2009 - 07:36 PM

I'm still worried when it comes to things like this. For one, GM was never designed with threading in mind, so it's extremely likely that the runner code is not thread-safe, making even simple assignment operations non-atomic (and, thus, dangerous).
  • 0

#13 English Guy

English Guy

    GMC Member

  • New Member
  • 4 posts

Posted 11 February 2009 - 08:42 PM

And i think that it's just epic phayul. Decompiled Snake's GM Threads that's used in The Legends of Arkanoid.
Procedures are very similiar, almost same :lol:

Edited by English Guy, 11 February 2009 - 08:43 PM.

  • 0

#14 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 11 February 2009 - 11:43 PM

And i think that it's just epic phayul. Decompiled Snake's GM Threads that's used in The Legends of Arkanoid.
Procedures are very similiar, almost same :lol:

I have included Windows API procedures, with the addition of Execute_string. If you really want proof, I could release another version now which can locate other Game Maker APIs. The problem is, I've only written the example in GM7 and it's going to take a while to convert, on top of all my current coursework.

If you're curious, here's how similar Windows API procedures are to the GM function calls:
DWORD SetThreadAffinityMask(HANDLE hThread, DWORD dwThreadAffinityMask);
DWORD SetThreadIdealProcessor(HANDLE hThread, DWORD dwIdealProcessor);
BOOL SetThreadPriority(HANDLE hThread, int nPriority);
BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);
Now do you see why some of the functions look so similar?
It's pretty simple to wrap some of those calls (>click for small relevant portion of DLL source code<)
Decompilation of machine code is pretty darn near impossible, and if you compare the 2 DLLs in a debugger, you will see that the procedures are completely different. And they're written in completely different programming languages. If this was a decompilation, then it's also unlikely that I'd have the knowledge to isolate and re-wrap the call to execute_string.

So why the sudden attack on the DLL? Where did your suspicions arise? Why don't you attack the countless mouse-movement or show-message DLLs, accusing them of being decompilations of each other?
Well, I suppose I should take it as a compliment - you're implying that this DLL should have been too difficult for me to make... I've surpassed myself! :P

All the thread function does is call execute_string - it's not that difficult once you have the knowledge in place.
If you want proof that this research is original, either ask Clam or talk to me privately (PM).

Please refrain from posting unfounded accusations.
(We all make mistakes, and though that was pretty insulting, I won't hold it against you. If you still think this is all one huge lie-plus-cover-up, don't hesitate to PM me.)

EDIT: Haha, found a bug in the code... thread_affinity is taking char* instead of double.
EDIT2: Oh, I've just seen your sig, and that your post was half shameless-game-plug too.

Edited by score_under, 11 February 2009 - 11:49 PM.

  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#15 English Guy

English Guy

    GMC Member

  • New Member
  • 4 posts

Posted 12 February 2009 - 12:55 AM

Okay i believe you, but it only looks similiar :lol:
  • 0

#16 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 19 February 2009 - 07:08 PM

I'd like to inform everyone of a small-but-tasty update with an example. If you're unsure of the details but good with ASM, then PM me and I could help you write your own function.
  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#17 ShadowAndroidX

ShadowAndroidX

    GMC Member

  • Validating
  • 56 posts

Posted 21 March 2009 - 11:03 AM

Nice work! I might use this sometime... :GM072:

#18 Samscam

Samscam

    Retired GMC Reviewer

  • GMC Member
  • 648 posts

Posted 22 March 2009 - 05:01 PM

Do you know of any bugs with alarms set in a seperate thread?

This code will cause my computer to freeze so I have to restart:
(Note where the alarms are set, below all global vars)
PRE
ldstr = "
global.FlipMenuBg = background_add_alpha(working_directory + '\Data\menu_.png', false);
global.whiteTex = sprite_add(working_directory + '\Data\Textures\white.jpg', 1, false, false, false, false, 0, 0);
global.sunTex = sprite_add(working_directory + '\Data\Textures\sun.jpg', 1, false, false, false, false, 0, 0);
global.mercuryTex = sprite_add(working_directory + '\Data\Textures\mercury.jpg', 1, false, false, false, false, 0, 0);
global.venusTex = sprite_add(working_directory + '\Data\Textures\venus.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[0] = sprite_add(working_directory + '\Data\Textures\earth_day.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[1] = sprite_add(working_directory + '\Data\Textures\earth_night.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[2] = sprite_add(working_directory + '\Data\Textures\earth_sea.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[3] = sprite_add(working_directory + '\Data\Textures\earth_clouds.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[4] = sprite_add(working_directory + '\Data\Textures\earth_clouds_alpha.jpg', 1, false, false, false, false, 0, 0);
global.marsTex = sprite_add(working_directory + '\Data\Textures\mars.jpg', 1, false, false, false, false, 0, 0);
global.jupiterTex = sprite_add(working_directory + '\Data\Textures\jupiter.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[0] = sprite_add(working_directory + '\Data\Textures\saturn.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[1] = sprite_add(working_directory + '\Data\Textures\saturn_ring.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[2] = sprite_add(working_directory + '\Data\Textures\saturn_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.shadow[0] = sprite_add(working_directory + '\Data\Textures\shadow.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[0] = sprite_add(working_directory + '\Data\Textures\uranus.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[1] = sprite_add(working_directory + '\Data\Textures\uranus_ring.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[2] = sprite_add(working_directory + '\Data\Textures\uranus_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.neptuneTex[0] = sprite_add(working_directory + '\Data\Textures\neptune.jpg', 1, false, false, false, false, 0, 0);
with(obj_load_begin){ loadbar_progress = 25; alarm[1] = 5; }
";
ld[0] = thread_exec(ldstr, "NORMAL");
thread_priority(ld[0], -1);
thread_suspend(ld[0], "RESUME");


This code will work SOME times:
PRE
ldstr = "
global.FlipMenuBg = background_add_alpha(working_directory + '\Data\menu_.png', false);
global.whiteTex = sprite_add(working_directory + '\Data\Textures\white.jpg', 1, false, false, false, false, 0, 0);
global.sunTex = sprite_add(working_directory + '\Data\Textures\sun.jpg', 1, false, false, false, false, 0, 0);
global.mercuryTex = sprite_add(working_directory + '\Data\Textures\mercury.jpg', 1, false, false, false, false, 0, 0);
global.venusTex = sprite_add(working_directory + '\Data\Textures\venus.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[0] = sprite_add(working_directory + '\Data\Textures\earth_day.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[1] = sprite_add(working_directory + '\Data\Textures\earth_night.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[2] = sprite_add(working_directory + '\Data\Textures\earth_sea.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[3] = sprite_add(working_directory + '\Data\Textures\earth_clouds.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[4] = sprite_add(working_directory + '\Data\Textures\earth_clouds_alpha.jpg', 1, false, false, false, false, 0, 0);
global.marsTex = sprite_add(working_directory + '\Data\Textures\mars.jpg', 1, false, false, false, false, 0, 0);
global.jupiterTex = sprite_add(working_directory + '\Data\Textures\jupiter.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[0] = sprite_add(working_directory + '\Data\Textures\saturn.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[1] = sprite_add(working_directory + '\Data\Textures\saturn_ring.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[2] = sprite_add(working_directory + '\Data\Textures\saturn_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.shadow[0] = sprite_add(working_directory + '\Data\Textures\shadow.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[0] = sprite_add(working_directory + '\Data\Textures\uranus.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[1] = sprite_add(working_directory + '\Data\Textures\uranus_ring.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[2] = sprite_add(working_directory + '\Data\Textures\uranus_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.neptuneTex[0] = sprite_add(working_directory + '\Data\Textures\neptune.jpg', 1, false, false, false, false, 0, 0);
with(obj_load_begin){ loadbar_progress = 25; }
alarm[1] = 5;
";
ld[0] = thread_exec(ldstr, "NORMAL");
thread_priority(ld[0], -1);
thread_suspend(ld[0], "RESUME");


And this code will ALWAYS work:
PRE
ldstr = "
global.FlipMenuBg = background_add_alpha(working_directory + '\Data\menu_.png', false);
global.whiteTex = sprite_add(working_directory + '\Data\Textures\white.jpg', 1, false, false, false, false, 0, 0);
global.sunTex = sprite_add(working_directory + '\Data\Textures\sun.jpg', 1, false, false, false, false, 0, 0);
global.mercuryTex = sprite_add(working_directory + '\Data\Textures\mercury.jpg', 1, false, false, false, false, 0, 0);
global.venusTex = sprite_add(working_directory + '\Data\Textures\venus.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[0] = sprite_add(working_directory + '\Data\Textures\earth_day.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[1] = sprite_add(working_directory + '\Data\Textures\earth_night.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[2] = sprite_add(working_directory + '\Data\Textures\earth_sea.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[3] = sprite_add(working_directory + '\Data\Textures\earth_clouds.jpg', 1, false, false, false, false, 0, 0);
global.earthTex[4] = sprite_add(working_directory + '\Data\Textures\earth_clouds_alpha.jpg', 1, false, false, false, false, 0, 0);
global.marsTex = sprite_add(working_directory + '\Data\Textures\mars.jpg', 1, false, false, false, false, 0, 0);
global.jupiterTex = sprite_add(working_directory + '\Data\Textures\jupiter.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[0] = sprite_add(working_directory + '\Data\Textures\saturn.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[1] = sprite_add(working_directory + '\Data\Textures\saturn_ring.jpg', 1, false, false, false, false, 0, 0);
global.saturnTex[2] = sprite_add(working_directory + '\Data\Textures\saturn_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.shadow[0] = sprite_add(working_directory + '\Data\Textures\shadow.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[0] = sprite_add(working_directory + '\Data\Textures\uranus.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[1] = sprite_add(working_directory + '\Data\Textures\uranus_ring.jpg', 1, false, false, false, false, 0, 0);
global.uranusTex[2] = sprite_add(working_directory + '\Data\Textures\uranus_ring_alpha.jpg', 1, false, false, false, false, 0, 0);
global.neptuneTex[0] = sprite_add(working_directory + '\Data\Textures\neptune.jpg', 1, false, false, false, false, 0, 0);
with(obj_load_begin){ loadbar_progress = 25; }
";
ld[0] = thread_exec(ldstr, "NORMAL");
thread_priority(ld[0], -1);
thread_suspend(ld[0], "RESUME");


Very strange I find.

Grtz,
Sam

Edited by samscam, 22 March 2009 - 05:02 PM.

  • 0

#19 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 25 March 2009 - 05:22 PM

Do you know of any bugs with alarms set in a seperate thread?

This code will cause my computer to freeze so I have to restart:
(Note where the alarms are set, below all global vars)

PRE
Code 1


This code will work SOME times:
PRE
Code 2


And this code will ALWAYS work:
PRE
Code 3


Very strange I find.

Grtz,
Sam

I'm afraid this is one of the many bugs you'll have to work around when using threads in Game Maker. Code 2 should not ever be used, because the code isn't executed in the context of an object, code 1 seems fine but doesn't set the alarm, and code 3 is the same.

Also, "NORMAL" and "RESUME" should be entered without quotes. After starting a thread as NORMAL, it's not necessary to RESUME it either. (You can start threads as SUSPENDED instead).
  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#20 Samscam

Samscam

    Retired GMC Reviewer

  • GMC Member
  • 648 posts

Posted 25 March 2009 - 05:44 PM

Code 1 DOES set the alarm in an object context:

with(obj_load_begin){ loadbar_progress = 25; alarm[1] = 5; }

But that is when my laptop (With a dual-core processor) freezes. It works fine on my "singel-core" desktop... If I remove the alarms from the code, it also works on my laptop...
  • 0

#21 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 25 March 2009 - 08:54 PM

Code 1 DOES set the alarm in an object context:

with(obj_load_begin){ loadbar_progress = 25; alarm[1] = 5; }

But that is when my laptop (With a dual-core processor) freezes. It works fine on my "singel-core" desktop... If I remove the alarms from the code, it also works on my laptop...

It doesn't freeze on my dual-core desktop... but I don't know why alarms can't be set, it's probably just something you'll have to work around.

Edited by score_under, 25 March 2009 - 08:55 PM.

  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#22 SerMSYS

SerMSYS

    GMC Member

  • New Member
  • 180 posts

Posted 13 April 2009 - 10:11 AM

Game Maker freezes when I initialize a thread that is suspended (but it works in normal).
And it suffers from the same script problems as GMThreads :o
I can only think of expanding user scripts to default functions or extension packages as they work properly in threads.


btw, it would be nice if you added a function to check if there was an error in a thread

Edited by SerMSYS, 13 April 2009 - 09:17 PM.

  • 0
Battlefield 2/CoD 4/Fallout 3/GTA IV player | Quad-Core owner

#23 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 13 April 2009 - 09:53 PM

And it suffers from the same script problems as GMThreads :)

This is one of the problems with GM's runner - it wasn't designed for multi threading, and it would take months of work to get it fixed without cooperation from Yoyo.

btw, it would be nice if you added a function to check if there was an error in a thread

I do already have all the data I need to get this working, but it would be a very hacky solution, and I'm not at home right now so I can't just start developing right away.
  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#24 Krisando

Krisando

    GMC Member

  • New Member
  • 1351 posts

Posted 30 December 2009 - 12:27 AM

Absolutely brilliant, memory manipulation just what I wanted. :whistle:
  • 0

Posted Image


#25 Gamer_X

Gamer_X

    GMC Member

  • New Member
  • 18 posts

Posted 05 July 2010 - 01:18 AM

So i'm using the DLL to execute threads which contain simple instructions or scripts, but looking here, i couldn't find a way to safely terminate the threads...in windows task-manager, i guess they appear as "plug-in container.exe"

Anyway, i found this code (on game end event) in the examples that shipped with the DLL:

a=current_time;
while(thread_count())
{
global.count=200;//invalidate loops
if(current_time-a>=10000)break;
sleep(1);
}
unload_execs(); //This is for emergencies only
//unload_execs() does NOT terminate threads correctly.

Is that it ? You wait a determined time to see if all threads end on their own and if not you use unload_execs as emergency measure ? Don't i have to put an exit instruction or something similar when my script ends ? Sorry if i got all wrong...

UPDATE: checking again today, i understand how it works, except for: (see below)

i'm trying to find the meaning of global.count=200;//invalidate loops but yet no sucess...:(

Edited by Gamer_X, 13 July 2010 - 05:31 PM.

  • 0

#26 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 15 July 2010 - 10:11 PM

UPDATE: checking again today, i understand how it works, except for: (see below)

i'm trying to find the meaning of global.count=200;//invalidate loops but yet no sucess...:(

(Sorry for not noticing the reply until now ;) )
If you look in the threads I'm running, their loop condition is: "while(global.count<200)".

By setting global.count to 200, it makes them come out of a loop and gracefully exit the script.
  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#27 Gamer_X

Gamer_X

    GMC Member

  • New Member
  • 18 posts

Posted 17 July 2010 - 02:04 PM

UPDATE: checking again today, i understand how it works, except for: (see below)

i'm trying to find the meaning of global.count=200;//invalidate loops but yet no sucess...:(

(Sorry for not noticing the reply until now ;) )
If you look in the threads I'm running, their loop condition is: "while(global.count<200)".

By setting global.count to 200, it makes them come out of a loop and gracefully exit the script.


Thanks for the answer :)


UPDATE:

so thread_init is only for creating dlls

Edited by Gamer_X, 17 July 2010 - 06:33 PM.

  • 0

#28 Gamer_X

Gamer_X

    GMC Member

  • New Member
  • 18 posts

Posted 17 July 2010 - 07:05 PM

I'm trying to make the dll execute a (controlled) infinite loop, because i want to run objects step codes in other threads, to avoid slowdown, but whatever i do, it doesn't execute as a loop (instead, it runs only once)

for example:

global.count=0

thread_exec(get_string("while(global.count<200){global.count=1;event_perform (obj_star.ev_other,obj_star.ev_user0);}global.count=0;",""));

and in obj_star.ev_other -> ev_user0 i put my step code for obj_star

Can someone help ?

Edited by Gamer_X, 17 July 2010 - 07:06 PM.

  • 0

#29 score_under

score_under

    Least kawaii

  • GMC Member
  • 1321 posts

Posted 17 July 2010 - 10:19 PM

I'm trying to make the dll execute a (controlled) infinite loop, because i want to run objects step codes in other threads, to avoid slowdown, but whatever i do, it doesn't execute as a loop (instead, it runs only once)

for example:

global.count=0

thread_exec(get_string("while(global.count<200){global.count=1;event_perform (obj_star.ev_other,obj_star.ev_user0);}global.count=0;",""));

and in obj_star.ev_other -> ev_user0 i put my step code for obj_star

Can someone help ?

There's not much point to using get_string there, but I suggest you rephrase it:

global.GameEnding = false;
thread_exec("while (!global.GameEnding){ event_perform(obj_star.ev_other,obj_star.ev_user0); sleep(10); }");
The "sleep(10)" slows down the loop by 10 milliseconds each time so that it doesn't hog the CPU.
As you can probably see, to end this thread you would simply set global.GameEnding = true; and wait for the thread count to go down.
  • 0

Anti-Decompiler for GM6.1 to GM8.1.91! :GM8_new: [Main skin by Sindarin]
Discontinued.

decimal2.png
^ Signature image because it's been sorta empty since the old host died

If you need to contact me, I still get notification emails from PMs.


#30 Gamer_X

Gamer_X

    GMC Member

  • New Member
  • 18 posts

Posted 17 July 2010 - 11:17 PM

I'm trying to make the dll execute a (controlled) infinite loop, because i want to run objects step codes in other threads, to avoid slowdown, but whatever i do, it doesn't execute as a loop (instead, it runs only once)

for example:

global.count=0

thread_exec(get_string("while(global.count<200){global.count=1;event_perform (obj_star.ev_other,obj_star.ev_user0);}global.count=0;",""));

and in obj_star.ev_other -> ev_user0 i put my step code for obj_star

Can someone help ?

There's not much point to using get_string there, but I suggest you rephrase it:

global.GameEnding = false;
thread_exec("while (!global.GameEnding){ event_perform(obj_star.ev_other,obj_star.ev_user0); sleep(10); }");
The "sleep(10)" slows down the loop by 10 milliseconds each time so that it doesn't hog the CPU.
As you can probably see, to end this thread you would simply set global.GameEnding = true; and wait for the thread count to go down.


EDITED/UPDATE:

Thanks a lot ! :) I did what you posted, your code is much more clear. Still didn't work as i intended, because there must be something wrong with my code. Although when my code is in the step event, it works. The idea is that my object goes slowly from the top to the bottom of the screen. When it reaches the bottom it appears at the top, still outside the room, on a randomized position and color, and then starts to go to the bottom again, and repeats this procedure until the level ends. But what happens is that the object only appears at the top and goes to the bottom once, and stays there... :( I also tried creating the object as global, but it didn't work.

My step code is:

if y > room_height
{
y = - irandom(250) - 1;
x = irandom(room_width) ;
image_blend = make_color_rgb(irandom(256),irandom(256),irandom(256));
}

//draw_sprite_ext(spr_star5,- 1,x,y,1,1,direction,image_blend,1) ;

My create code: (it works)

speed = 0.5 ;
direction = 270 ;
y = - irandom(250) - 1;
x = irandom(room_width) ;
image_blend = make_color_rgb(irandom(256),irandom(256),irandom(256));


I will try messing with it later

Edited by Gamer_X, 19 July 2010 - 04:07 PM.

  • 0