Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [A5] Threading a game

Credits go to Edgar Reynaldo and Thomas Fjellstrom for helping out!
This thread is locked; no one can reply to it. rss feed Print
 1   2 
[A5] Threading a game
J-Gamer
Member #12,491
January 2011
avatar

I have been trying to wrap my head around threads and I have come up with two questions:
1. Is there a way to send events from one thread to another? In particular user events, so that I can send costum information(name of button pressed, upgrade used, etc.). I was thinking of having an event queue in the shared data class which would listen to such user events.
2. Is it possible to use functions of an object as the main function of a thread? I could than have a AI::Think function which would try to find the other player's cannons etc.

Also, according to this thread, threading can make modularising your application way easier if you got the mutexes right. Did I understand that correctly?

Any help?

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

weapon_S
Member #7,859
October 2006
avatar

1. Send them all to a 'global' queue. "Listening" is a big word.
2. With Boost maybe? No, otherwise.

I don't know what the link is about. Just fiddle around until you understand it ???

J-Gamer
Member #12,491
January 2011
avatar

weapon_S said:

Send them all to a 'global' queue.

How do you mean? What I thought of doing was having an event queue in a data class(where you can put a mutex on) and sending costum events to it so that the parent thread can get informed about what happened.

Example of usage:
1. Game initialisation
2. Main thread starts AI thread
3. AI thinks while main thread is handling user input etc.
4. AI wants to make a move and sends an event with all necessary data in it to the event queue in the data class
5. main thread listens to the event queue(how should this happen? there is always the possibility of a mutex being on the queue at the time) in the data class and reacts accordingly by making the move.
6. Go back to step 3

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Thomas Fjellstrom
Member #476
June 2000
avatar

Yes, I believe event queues were explicitly designed to be thread safe. So you can send events from one thread and receive events in another.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

J-Gamer
Member #12,491
January 2011
avatar

I just came up with this to let this happen:
in the data class, I should have an ALLEGRO_USER_EVENT_SOURCE* member, which is set when the thread first starts. Then, in the main thread, I register this event source in the event queue of the main thread, and whenever the AI makes its move, it uses al_emit_user_event to notify the main thread it made a move.

Is my logic sound/should this work?

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Thomas Fjellstrom
Member #476
June 2000
avatar

Yup, though you may also have the option of emitting fake input events. Which ever works best for your case.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

J-Gamer
Member #12,491
January 2011
avatar

I think the AI will want to just say what he wants to do, instead of faking a mouse click or something like that. I'm also thinking of putting user input in another thread with the same principle: as soon as the player really chooses to send a unit(scout,bomb,other upgrade) to the field, the main thread will be noticed trough a user event.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Thomas Fjellstrom
Member #476
June 2000
avatar

Some people prefer to simulate input events for AI, so it can be recorded in the same way user input is recorded, for replays, save games, and cheat detection. But I think if you're emitting user events for the player as well, you can use those to do all that instead of raw input.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

J-Gamer
Member #12,491
January 2011
avatar

Yeah... I think it is easier to let the user input thread figure out what the user wants to do and then send the corresponding event to the main thread. Otherwise, I would have to put extra code in at the AI output part to calculate all the coordinates of mouse clicks etc.

One technical question that still hasn't been answered: How do I make a function pointer to a member function? Because that's what I'll need if I want to use the AI's Think() function as main function of the thread.

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

J-Gamer said:

2. Is it possible to use functions of an object as the main function of a thread? I could than have a AI::Think function which would try to find the other player's cannons etc.

Yes, you can use class methods, they just have to be static, and you have to bind the parameters somehow.

#SelectExpand
1 2class ArgumentBinder { 3private : 4 int p1; 5 Data* d; 6public : 7 static void Process(ALLEGRO_THREAD* thread , void* this) { 8 ((ArgumentBinder*)this)->ClassMethod(); 9 } 10 11 void ClassMethod() { 12 // do stuff with members 13 } 14} 15 16ArgumentBinder bound_data; 17ALLEGRO_THREAD* thread = al_create_thread(ArgumentBinder::Process , &bound_data);

J-Gamer
Member #12,491
January 2011
avatar

Thanks! I'll try that.

EDIT:
This is what I've come up with so far:

#SelectExpand
1#ifndef __THREADED_H__ 2#define __THREADED_H__ 3class Threaded; 4 5class ThreadedData 6{ 7 public: 8 ALLEGRO_MUTEX* mutex; 9 ALLEGRO_COND* cond; 10 bool ready; 11 Threaded* thr; //used in Threaded::InitThread 12 ThreadedData() : mutex(al_create_mutex()),cond(al_create_cond()),ready(false); 13}; 14 15class Threaded 16{ 17 private: 18 public: 19 static void InitThread(ALLEGRO_THREAD* thr, void* data) 20 { 21 (ThreadedData*)data->thr->Thread_function((ThreadedData*)data); 22 }; 23 virtual void Thread_function(ALLEGRO_THREAD* thr,ThreadedData* data) {}; 24}; 25#endif

These are classes you should derive from, like this:

#SelectExpand
1class ThreadedUIData : public ThreadedData 2{ 3 public: 4 //other variables you need to be shared between your class and the parent thread 5}; 6 7class ThreadedUI : public ThreadedData 8{ 9 private: 10 ALLEGRO_EVENT_QUEUE* event_queue; 11 12 public: 13 void Thread_function(ALLEGRO_THREAD* thr,ThreadedData* data); 14}; 15 16void ThreadedUI::Thread_function(ALLEGRO_THREAD* thr, ThreadedData* data) 17{ 18 //initialize variables 19 event_queue = al_create_event_queue(); 20 al_register_event_source(event_queue,al_get_mouse_event_source()); 21 al_register_event_source(event_queue,al_get_keyboard_event_source()); 22 23 24 al_lock_mutex(data->mutex); 25 ThreadedUIData* ui_data = (ThreadedUIData*)data; 26 //do something with the fields of ThreadedUIData* ui_data 27 al_signal_cond(data->cond); 28 data->ready = true; 29 al_unlock_mutex(data->mutex); 30 31 //main thread loop 32 while(!al_thread_should_stop(thr)) 33 { 34 //check the event queue for input events and send a costum event if something important has been selected 35 } 36};

Or am I doing something terribly wrong here?

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

BAF
Member #2,981
December 2002
avatar

Some people prefer to simulate input events for AI, so it can be recorded in the same way user input is recorded, for replays, save games, and cheat detection.

Err, what? I've never heard of that before. ???

Even if you did do that, how would you prevent the user input from affecting the AI player?

Arthur Kalliokoski
Second in Command
February 2005
avatar

IIRC that's how they did it in Quake, all actors have the same input interface, some are controlled by AI.

They all watch too much MSNBC... they get ideas.

Thomas Fjellstrom
Member #476
June 2000
avatar

BAF said:

Even if you did do that, how would you prevent the user input from affecting the AI player?

The AI is essentially just another player with access to no more control or information as the player is.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

BAF
Member #2,981
December 2002
avatar

Okay, but why would you simulate input events? How would you prevent the user from making those same inputs, thereby screwing wth the AI?

Arthur Kalliokoski
Second in Command
February 2005
avatar

The "inputs" to an actor are just software interfaces, not actually hardwired to the mouse or anything.

They all watch too much MSNBC... they get ideas.

BAF
Member #2,981
December 2002
avatar

Yes, but TF was talking about emitting fake input events. If you're faking input events to control the AI, then it looks the same as normal input events.

Arthur Kalliokoski
Second in Command
February 2005
avatar

The AI actors inputs are sent "events" by the AI. Your protagonist actor is sent "events" from the mouse and keyboard, etc.

Think of a computer controlled car that has servos hooked to the steering wheel and pedals, and you're not in it. You race this car by driving another car normally.

They all watch too much MSNBC... they get ideas.

Thomas Fjellstrom
Member #476
June 2000
avatar

BAF said:

Yes, but TF was talking about emitting fake input events. If you're faking input events to control the AI, then it looks the same as normal input events.

Depends. If you went that low level, you could simulate a second mouse/keyboard. But "Input Events" does not necessarily mean low level input events. But yeah, if Allegro doesn't have a way to distinguish between multiple low level input devices, that would make it difficult to go that low level.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

BAF
Member #2,981
December 2002
avatar

Yes, I understand. I am questioning TF's solution of handling threading by putting fake input events into the queue from the other thread.

Matthew Leverton
Supreme Loser
January 1999
avatar

This:

But "Input Events" does not necessarily mean low level input events

It's not a very hard concept to grasp. :P

BAF
Member #2,981
December 2002
avatar

The original description was ambiguous at best, and that was what I was referring to. I understand the intended meaning...

Some people prefer to simulate input events for AI, so it can be recorded in the same way user input is recorded, for replays, save games, and cheat detection.

Trent Gamblin
Member #261
April 2000
avatar

I don't think you would put them on the same queue. For example, you could have an Entity class that has a ALLEGRO_EVENT *GetInput(void) method that could return ALLEGRO_EVENTS like key presses and releases and the "Game" class would handle them. Then you can do things like:

for (i = 0; i < num_entities; i++) {
    ALLEGRO_EVENT *e = entity[i]->GetInput();
    if (e) {
        if (e->type == ALLEGRO_EVENT_KEYBOARD) {
            switch (e->keyboard.keycode) {
                ...
            }
        }
    }
}

And you could have some typedef:

typedef ALLEGRO_EVENT *(input_getter)(void);

Then your player class could have

private:
    input_getter input;

And when you wanted to change input you could just set 'input' to PhysicalInput(), ScriptInput(), NoInput() or whatever.

None of this really relies on using ALLEGRO_EVENTs though. But generally having a standard interface to get input from can be useful.

Thomas Fjellstrom
Member #476
June 2000
avatar

BAF said:

The original description was ambiguous at best, and that was what I was referring to. I understand the intended meaning...

It was meant to cover both low level injecting, and using higher level events, but both would still be input simulation as far as the engine is concerned.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

J-Gamer
Member #12,491
January 2011
avatar

Bugfix: The code I posted had 2 errors, which are fixed in this renewed code:

#SelectExpand
1#ifndef __THREADED_H__ 2#define __THREADED_H__ 3class Threaded; 4 5class ThreadedData 6{ 7 public: 8 ALLEGRO_MUTEX* mutex; 9 ALLEGRO_COND* cond; 10 bool ready; 11 Threaded* thr; //used in Threaded::InitThread 12 ThreadedData() : mutex(al_create_mutex()),cond(al_create_cond()),ready(false) {}; 13}; 14 15class Threaded 16{ 17 private: 18 public: 19 static void InitThread(ALLEGRO_THREAD* thr, void* data) 20 { 21 ((ThreadedData*)data)->thr->Thread_function(thr,(ThreadedData*)data); 22 }; 23 virtual void Thread_function(ALLEGRO_THREAD* thr,ThreadedData* data) = 0; 24}; 25 26class ThreadedUIData : public ThreadedData 27{ 28 public: 29 //other variables you need to be shared between your class and the parent thread 30}; 31 32class ThreadedUI : public ThreadedData 33{ 34 private: 35 ALLEGRO_EVENT_QUEUE* event_queue; 36 37 public: 38 void Thread_function(ALLEGRO_THREAD* thr,ThreadedData* data); 39}; 40 41void ThreadedUI::Thread_function(ALLEGRO_THREAD* thr, ThreadedData* data) 42{ 43 //initialize variables 44 event_queue = al_create_event_queue(); 45 al_register_event_source(event_queue,al_get_mouse_event_source()); 46 al_register_event_source(event_queue,al_get_keyboard_event_source()); 47 48 49 ThreadedUIData* ui_data = (ThreadedUIData*)data; 50 al_lock_mutex(ui_data->mutex); 51 //do something with the fields of ThreadedUIData 52 ui_data->ready = true; 53 al_signal_cond(ui_data->cond); 54 al_unlock_mutex(ui_data->mutex); 55 56 //main thread loop 57 while(!al_get_thread_should_stop(thr)) 58 { 59 //do stuff 60 } 61}; 62 63#endif

EDIT: about input, I'm thinking of making costum events for using upgrades(scout,bomb,tunneler,...) so that those can be sent to the main game logic thread instead of raw input. I'll let the UI thread find out which upgrade the player wants to place where.(High-level input FTW ;D)

EDIT2: Are allegro calls thread-safe?(Like al_get_display_width/height,...)

" There are plenty of wonderful ideas in The Bible, but God isn't one of them." - Derezo
"If your body was a business, thought would be like micro-management and emotions would be like macro-management. If you primarily live your life with emotions, then you are prone to error on the details. If you over-think things all the time you tend to lose scope of priorities." - Mark Oates

 1   2 


Go to: