Game_loop
< Newer Topic
:: Older Topic >
#1 Feb 9, 2010 6:29 pm
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
I'm going through trying to clean up game_loop and I really like how AFK does things. Particularly this:
I was curious as to if there was any way to do this in C - particularly SWR?
if( dlist.size( ) > 0 ) process_input( );
I was curious as to if there was any way to do this in C - particularly SWR?
#2 Feb 9, 2010 8:35 pm
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
Nope, because that's using std::list.
C doesn't have that. Gotta be C++
There might be an extremely ugly way to pull it off using the C Linked lists, but it's not going to look clean like that.
C doesn't have that. Gotta be C++
There might be an extremely ugly way to pull it off using the C Linked lists, but it's not going to look clean like that.
#3 Feb 9, 2010 8:36 pm
Last edited Feb 9, 2010 8:38 pm by Andril
Magician
GroupMembers
Posts147
JoinedJun 9, 2009
Why not do a check against maxdesc > 0?
#4 Feb 9, 2010 8:41 pm
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Is it possible to add std::list without a full conversion to C++?
I'm not QUITE ready to do the full conversion until I understand a bit more.
I'm not QUITE ready to do the full conversion until I understand a bit more.
#5 Feb 9, 2010 8:45 pm
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
Keirath said:
Is it possible to add std::list without a full conversion to C++?
I'm not QUITE ready to do the full conversion until I understand a bit more.
Yes. You merely need to be compiling using g++ to use the STL. However, it is highly recommended that anything using a member of the STL use new/delete and have an appropriate constructor/destructor to avoid.. complications.
#6 Feb 9, 2010 9:15 pm
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Right, I can handle that. Thanks.
#7 Feb 10, 2010 10:08 am
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Well, I'm thinking I'm just going to attempt to convert SWR1.3FUSS to C++. I figure why not. I'm not running an active MUD and am just programming for the heck of it right now. With hopes of sometime opening one in the future (who knows). So, I know Samson did this with AFK and some others have done this. Where should I start? Some pointers would be great.
#8 Feb 10, 2010 10:26 am
Last edited Feb 10, 2010 10:29 am by Kayle
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
You should start by not doing what I did with MW. Which was, pick a struct, convert it to a class and then proceed to completely encapsulate it. That was a nightmare. [Edit:] Don't get me wrong, now that it's done it's beautiful. The act of getting to that point was horrible. It took close to three weeks to completely finish encapsulating it. And I mean completely. Most every function that took CHAR_DATA as the only argument was turned into a member function. Everything was replaced with get/set functions. Completely encapsulated. Nightmare. Total Nightmare. You never really understand the term "spaghetti code" until you attempt this...
The easiest way to start is to figure out what should/shouldn't be a class over a struct. Once you know, change them. Give them constructors/destructors. Make sure the c'tor/d'tor work fine. Test for memory issues, and then move to the next class. Once you've got those figured out, the next step would be to pick a part of the STL and apply it.
However, if there's one thing I've learned in the... 3 times I've done this process so far. Only convert what you're working on. Unless your goal is to simply convert the entire codebase to use the STL, in which case, start with what you know, and work from there.
There are several things to keep in mind as well. Certain converstions (std::bitset comes to mind) may require rewriting the way things are handled in the core (affects comes to mind...) because of how things were originally designed. You have to keep in mind that these bases were not designed with C++, the STL, or Object Oriented programming in mind. Converting these bases to C++ is a massive undertaken, and not one that should be taken lightly.
Also worth noting is that AFKMud never completely converted to using the STL. I believe Samson only finished a third or so of the std::string replacement.
The easiest way to start is to figure out what should/shouldn't be a class over a struct. Once you know, change them. Give them constructors/destructors. Make sure the c'tor/d'tor work fine. Test for memory issues, and then move to the next class. Once you've got those figured out, the next step would be to pick a part of the STL and apply it.
However, if there's one thing I've learned in the... 3 times I've done this process so far. Only convert what you're working on. Unless your goal is to simply convert the entire codebase to use the STL, in which case, start with what you know, and work from there.
There are several things to keep in mind as well. Certain converstions (std::bitset comes to mind) may require rewriting the way things are handled in the core (affects comes to mind...) because of how things were originally designed. You have to keep in mind that these bases were not designed with C++, the STL, or Object Oriented programming in mind. Converting these bases to C++ is a massive undertaken, and not one that should be taken lightly.
Also worth noting is that AFKMud never completely converted to using the STL. I believe Samson only finished a third or so of the std::string replacement.
#9 Feb 10, 2010 10:43 am
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
I think I'm going to go the route of converting what I'm working on. I guess the first step would be descriptor_data since that is where I am.
Sadlly, the point I'm at it seems like it would be smarter to rip AFK down to what I want it to be. So I'm not sure. We'll see.
Sadlly, the point I'm at it seems like it would be smarter to rip AFK down to what I want it to be. So I'm not sure. We'll see.
#10 Feb 10, 2010 10:44 am
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
Learn more by doing it yourself. But keep constant backups.
#11 Feb 10, 2010 10:57 am
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Yeah, probably the better option.
I'm going to start by converting Descriptors to classes and then encapsulate it. I guess this is the best option, guess I have a lot of figuring out to do.
I'm going to start by converting Descriptors to classes and then encapsulate it. I guess this is the best option, guess I have a lot of figuring out to do.
#12 Feb 10, 2010 11:07 am
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Gah, I'm having trouble just getting std::list set up in descriptors, I'm going to have to read up on this before I try.
#13 Feb 10, 2010 2:39 pm
Black Hand
GroupAdministrators
Posts3,697
JoinedJan 1, 2002
It's also probably not a good idea to use what I've done in AFKMud as an example of the right way. Most of the work focused on "does it function" and not "is this clean and proper AND functions too". I also never went to the point of encapsulating everything, but there are plenty of things that have been.
If ever there was an incarnation of spaghetti code, I'd say AFKMud qualifies. It's some kind of mutated hybrid of C and C++.
If ever there was an incarnation of spaghetti code, I'd say AFKMud qualifies. It's some kind of mutated hybrid of C and C++.
#14 Feb 10, 2010 2:57 pm
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Haha, well are there any bases that have done it the "right" way - or really any code examples or websites I should check? I am the type of person that learns by example. It's how I learned C. I am in the middle of converting descriptors to 'dlist' and so far I'm understanding everything. I know when I get into some other things it could be a pain.
#15 Feb 10, 2010 5:38 pm
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
SocketMUD++ might be worth looking at. I can't recall off the top of my head, and I can't get to MudBytes atm because of a serialization issue in my database entry.
#16 Feb 10, 2010 8:05 pm
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
So, can you explain more about what you mean by the new/delete and the constructors/destructors? Do you mean that DESCRIPTOR_DATA needs that or just dlist?
Do I need to rework all of descriptor_data to classes and the likes and use stuff like that or what?
Do I need to rework all of descriptor_data to classes and the likes and use stuff like that or what?
#17 Feb 10, 2010 8:50 pm
Magician
GroupMembers
Posts147
JoinedJun 9, 2009
In regards to the new/delete issue, I think he was saying that you need to make sure that anything created with new is removed using delete. Same thing with new[] and delete[]. It's similar to the STRALLOC/STRFREE vs str_dup/DELETE thing.
Check out this page for examples on using new and delete.
Check out this page for examples on using new and delete.
#18 Feb 10, 2010 11:02 pm
Black Hand
GroupAdministrators
Posts3,697
JoinedJan 1, 2002
new() and delete() are basically your substitutes for CREATE and DISPOSE for the actual structs.
You can also use new[] and delete[] on char* type strings within those structs, but it isn't strictly necessary.
If you're planning to use C++ STL code within things like descriptor_data then you can't keep using CREATE/DISPOSE on it or you'll crash the game.
You can also use new[] and delete[] on char* type strings within those structs, but it isn't strictly necessary.
If you're planning to use C++ STL code within things like descriptor_data then you can't keep using CREATE/DISPOSE on it or you'll crash the game.
#19 Feb 11, 2010 11:38 am
Off the Edge of the Map
GroupAdministrators
Posts1,200
JoinedMar 21, 2006
Well, this might be a bit over the top for an example, but here we go anyway. This is a Socket class I wrote for Elysium.
Alright, So then we have the actual functions:
Yes, I know it's not commented as well as it could be. But the whole thing is a work in progress. Anyway. If that makes sense, great. If not, well, when the cold medicine wears off I can try and explain it again. Or David can take a crack at it if he beats me to it.
/**************************************************************************** * ___________.__ .__ * * \_ _____/| | ___.__. _____|__|__ __ _____ * * | __)_ | |< | |/ ___/ | | \/ \ * * | \| |_\___ |\___ \| | | / Y Y \ * * /_______ /|____/ ____/____ >__|____/|__|_| / * * \/ \/ \/ Engine \/ * * ------------------------------------------------------------------------ * * Elysium Engine Copyright 1999-2010 by Steven Loar * * Elysium Engine Development Team: Kayle (Steven Loar) * * ------------------------------------------------------------------------ * * Socket Class Header * ****************************************************************************/ #ifndef __SOCKET_H__ #define __SOCKET_H__ #include#include "libtelnet.h" #include "strings.h" class Socket { private: Socket( const Socket & ); //Unused, but declared and made private so that an error gets thrown if it's ever called by accident Socket &operator = ( const Socket & ); //Unused, but declared and made private so that an error gets thrown if it's ever called by accident public: Socket( int desc ); //Constructor - called via new virtual ~Socket( ); //Destructor - called via delete //Member functions - Done so that there can be error checking, etc, inside the class itself //rather than in every function that tries to modify things Elysium::String getInBuffer( ) const { return m_InBuffer; } int getControl( ) const { return m_Control; } void clearInBuffer( ) { m_InBuffer.erase( 0, m_InBuffer.length( ) ); } void writeTo( Elysium::String txt ); bool readFrom( ); static void TelnetHandlerCallback( telnet_t *telnet, telnet_event_t *event, void *user_data ); void HandleTelnetEvent( telnet_event_t *event ); //Member fields - Private so that they can't be modified directly. private: int m_Control; Elysium::String m_InBuffer; telnet_t m_Telnet; }; #endif
Alright, So then we have the actual functions:
/**************************************************************************** * ___________.__ .__ * * \_ _____/| | ___.__. _____|__|__ __ _____ * * | __)_ | |< | |/ ___/ | | \/ \ * * | \| |_\___ |\___ \| | | / Y Y \ * * /_______ /|____/ ____/____ >__|____/|__|_| / * * \/ \/ \/ Engine \/ * * ------------------------------------------------------------------------ * * Elysium Engine Copyright 1999-2010 by Steven Loar * * Elysium Engine Development Team: Kayle (Steven Loar) * * ------------------------------------------------------------------------ * * Socket Class Module * ****************************************************************************/ #include#include #include #include "socket.h" static const telnet_telopt_t my_telopts[] = { { TELNET_TELOPT_ECHO, TELNET_WILL, TELNET_DONT }, { TELNET_TELOPT_TTYPE, TELNET_WILL, TELNET_DONT }, { TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DO }, { TELNET_TELOPT_MSSP, TELNET_WONT, TELNET_DO }, { TELNET_TELOPT_BINARY, TELNET_WILL, TELNET_DO }, { TELNET_TELOPT_NAWS, TELNET_WILL, TELNET_DONT }, { -1, 0, 0 } }; //Constructor - uses initializer list where applicable, initializes everything else in a regular way. Socket::Socket( int desc ) : m_Control( desc ) { memset( &m_Telnet, 0, sizeof( m_Telnet ) ); telnet_init( &m_Telnet, my_telopts, &Socket::TelnetHandlerCallback, 0, this ); telnet_negotiate( &m_Telnet, TELNET_WILL, TELNET_TELOPT_COMPRESS2 ); } //Destructor - Handles closing the socket, and freeing all data that isn't simple data (int, double, etc) Also calls destructors for STL members. Socket::~Socket( ) { if( m_Control != -1 ) close( m_Control ); telnet_free( &m_Telnet ); } void Socket::TelnetHandlerCallback(telnet_t *telnet, telnet_event_t *ev, void *user_data) { static_cast (user_data)->HandleTelnetEvent( ev ); } void Socket::HandleTelnetEvent( telnet_event_t *ev ) { switch( ev->type ) { case TELNET_EV_DATA: m_InBuffer.append( ev->buffer, ev->size ); break; case TELNET_EV_SEND: writeTo( m_Control, ev->buffer, ev->size ); break; case TELNET_EV_ERROR: //Really need to come up with some kind of error reporting/bug function... //fatal_error("TELNET error: %s", ev->buffer); break; default: break; } } bool Socket::readFrom( void ) { char temp[4096]; int size; while( true ) { size = read( m_Control, temp, sizeof( temp ) ); if( size > 0 ) { telnet_recv( &m_Telnet, temp, size ); } else if ( size == 0 ) break; else if ( errno == EAGAIN ) break; else return false; } return true; } void Socket::writeTo( Elysium::String txt ) { telnet_send( &m_Telnet, txt.c_str(), txt.length() ); }
Yes, I know it's not commented as well as it could be. But the whole thing is a work in progress. Anyway. If that makes sense, great. If not, well, when the cold medicine wears off I can try and explain it again. Or David can take a crack at it if he beats me to it.
#20 Feb 12, 2010 8:14 am
Magician
GroupMembers
Posts148
JoinedJan 24, 2008
Well, 3 days into it I totally botched my conversion of descriptors to classes. I think I took on too much at once. I'm not discouraged though - learning so much it's well worth it.
One of the biggest problems I found was when something would be a string and was being passed to another. Is this just where I should just worry about changing to strings later and concentrate on just setting up the classes and getting rid of create dispose in exchange for new/delete.
Also, someone recently mentioned using std::vector for linked lists instead of std::list. Any thoughts on this? I'm not sure how I feel about that but wanted to see what people think.
One of the biggest problems I found was when something would be a string and was being passed to another. Is this just where I should just worry about changing to strings later and concentrate on just setting up the classes and getting rid of create dispose in exchange for new/delete.
Also, someone recently mentioned using std::vector for linked lists instead of std::list. Any thoughts on this? I'm not sure how I feel about that but wanted to see what people think.