Page 1 of 1

New to game programming - Can't setup Multi-Threading

Posted: Thu Aug 02, 2018 4:01 am
by Dynasoul Studio
Ogre Version: 2.2
Operating System: Windows 10
Render System: DirectX

Code: Select all

Ogre.log (optional) <-- Don't know how to enable
Hey guys I'm jumping straight in to working/learning ogre and have successfully setup and built the standard template that cmake generates, defaulting to a single threaded application. I renamed all references to "Demo" to "SpaceRanchers" and successfully compiled and ran the application.

Image

I am learning how this stuff works by taking a "monkey see monkey do" approach. Conceptually my understanding of what the main .cpp file does as setup through the template is "Overload" the functions found within either MainLoopSingleThreaded.cpp or MainLoopMultiThreaded.cpp with all the custom code and game logic I'm planning to implement.

In other words, in my mind, MainLoopSingleThreaded.cpp and MainLoopMultiThreaded.cpp setup the basic barebones of a running game loop and I will be "building out" my project primarily in SpaceRanchers.cpp. This file, the only one I've edited, only works when told to use MainLoopSingleThreaded.cpp. Here it is:

Code: Select all

#define LL_NORMAL = 2
#include "GraphicsSystem.h"
#include "LogicSystem.h"
#include "SpaceRanchersGameState.h"

#include "OgreSceneManager.h"
#include "OgreCamera.h"
#include "OgreRoot.h"
#include "OgreRenderWindow.h"
#include "OgreConfigFile.h"
#include "Compositor/OgreCompositorManager2.h"

//Declares WinMain / main
#include "MainEntryPointHelper.h"
#include "System/MainEntryPoints.h"

#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <pwd.h>
    #include <errno.h>
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    #include "shlobj.h"
#endif




#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
INT WINAPI WinMainApp( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR strCmdLine, INT nCmdShow )
#else
int mainApp( int argc, const char *argv[] )
#endif
{
    return SpaceRanchers::MainEntryPoints::mainAppMultiThreaded( SPACERANCHERS_MAIN_ENTRY_PARAMS );
}

namespace SpaceRanchers
{
    class SpaceRanchersGraphicsSystem : public GraphicsSystem
    {
        virtual Ogre::CompositorWorkspace* setupCompositor()
        {
            return GraphicsSystem::setupCompositor();
        }

        virtual void setupResources(void)
        {
            GraphicsSystem::setupResources();

            Ogre::ConfigFile cf;
            cf.load(mResourcePath + "resources2.cfg");

            Ogre::String dataFolder = cf.getSetting( "DoNotUseAsResource", "Hlms", "" );

            if( dataFolder.empty() )
                dataFolder = "./";
            else if( *(dataFolder.end() - 1) != '/' )
                dataFolder += "/";

            dataFolder += "2.0/scripts/materials/PbsMaterials";

            addResourceLocation( dataFolder, "FileSystem", "General" );
        }

    public:
		SpaceRanchersGraphicsSystem(GameState *gameState, Ogre::ColourValue backgroundColour = Ogre::ColourValue(1.0f,0.0f,0.0f,1.0f)) :
            GraphicsSystem( gameState )
        {
			mResourcePath = getenv("APPDATA");
			mResourcePath += "/DynasoulStudio/SpaceRanchers/Data/";

            //It's recommended that you set this path to:
            //	%APPDATA%/SpaceRanchers/ on Windows
            //	~/.config/SpaceRanchers/ on Linux
            //	macCachePath() + "/SpaceRanchers/" (NSCachesDirectory) on Apple -> Important because
            //	on iOS your app could be rejected from App Store when they see iCloud
            //	trying to backup your Ogre.log & ogre.cfg auto-generated without user
            //	intervention. Also convenient because these settings will be deleted
            //	if the user removes cached data from the app, so the settings will be
            //	reset.
            //  Obviously you can replace "SpaceRanchers" by your app's name.
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            mWriteAccessFolder =  + "/";
            TCHAR path[MAX_PATH];
            if( SUCCEEDED( SHGetFolderPath( NULL, CSIDL_APPDATA, NULL,
                                            SHGFP_TYPE_CURRENT, path ) != S_OK ) )
            {
                //Need to convert to OEM codepage so that fstream can
                //use it properly on international systems.
        #if defined(_UNICODE) || defined(UNICODE)
                int size_needed = WideCharToMultiByte( CP_OEMCP, 0, path, (int)wcslen(path),
                                                       NULL, 0, NULL, NULL );
                mWriteAccessFolder = std::string( size_needed, 0 );
                WideCharToMultiByte( CP_OEMCP, 0, path, (int)wcslen(path),
                                     &mWriteAccessFolder[0], size_needed, NULL, NULL );
        #else
                TCHAR oemPath[MAX_PATH];
                CharToOem( path, oemPath );
                mWriteAccessFolder = std::string( oemPath );
        #endif
                mWriteAccessFolder += "/SpaceRanchers/";

                //Attempt to create directory where config files go
                if( !CreateDirectoryA( mWriteAccessFolder.c_str(), NULL ) &&
                    GetLastError() != ERROR_ALREADY_EXISTS )
                {
                    //Couldn't create directory (no write access?),
                    //fall back to current working dir
                    mWriteAccessFolder = "";
                }
            }
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
            const char *homeDir = getenv("HOME");
            if( homeDir == 0 )
                homeDir = getpwuid( getuid() )->pw_dir;
            mWriteAccessFolder = homeDir;
            mWriteAccessFolder += "/.config";
            int result = mkdir( mWriteAccessFolder.c_str(), S_IRWXU|S_IRWXG );
            int errorReason = errno;

            //Create "~/.config"
            if( result && errorReason != EEXIST )
            {
                printf( "Error. Failing to create path '%s'. Do you have access rights?",
                        mWriteAccessFolder.c_str() );
                mWriteAccessFolder = "";
            }
            else
            {
                //Create "~/.config/SpaceRanchers"
                mWriteAccessFolder += "/SpaceRanchers/";
                result = mkdir( mWriteAccessFolder.c_str(), S_IRWXU|S_IRWXG );
                errorReason = errno;

                if( result && errorReason != EEXIST )
                {
                    printf( "Error. Failing to create path '%s'. Do you have access rights?",
                            mWriteAccessFolder.c_str() );
                    mWriteAccessFolder = "";
                }
            }
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE || OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
            mWriteAccessFolder = macCachePath() + "/SpaceRanchers/";
            //Create "pathToCache/SpaceRanchers"
            mWriteAccessFolder += "/SpaceRanchers/";
            result = mkdir( mWriteAccessFolder.c_str(), S_IRWXU|S_IRWXG );
            errorReason = errno;

            if( result && errorReason != EEXIST )
            {
                printf( "Error. Failing to create path '%s'. Do you have access rights?",
                        mWriteAccessFolder.c_str() );
                mWriteAccessFolder = "";
            }
#endif
        }
    };

    void MainEntryPoints::createSystems( GameState **outGraphicsGameState,
                                         GraphicsSystem **outGraphicsSystem,
                                         GameState **outLogicGameState,
                                         LogicSystem **outLogicSystem )
    {

		//For Debugging -_-
		AllocConsole();
		freopen("conin$", "r", stdin);
		freopen("conout$", "w", stdout);
		freopen("conout$", "w", stderr);
		printf("Debugging Window:\n");
		
        SpaceRanchersGameState *gfxGameState = new SpaceRanchersGameState("Space Ranchers");
		GraphicsSystem *graphicsSystem = new SpaceRanchersGraphicsSystem( gfxGameState );
		LogicSystem *logicSystem = new LogicSystem(gfxGameState);
		GameEntityManager *gameEntityManager = new GameEntityManager(graphicsSystem, logicSystem);

		printf("Finished setting up createSystems()\nNotifying systems\n");
        gfxGameState->_notifyGraphicsSystem( graphicsSystem );
		graphicsSystem->_notifyLogicSystem( logicSystem );
		logicSystem->_notifyGraphicsSystem( graphicsSystem );
		printf("Finished Notifying Systems\n");
        *outGraphicsGameState = gfxGameState;
        *outGraphicsSystem = graphicsSystem;
		*outLogicSystem = logicSystem;
		printf("Finished Hooking Pointers\n");
    }

    void MainEntryPoints::destroySystems( GameState *graphicsGameState,
                                          GraphicsSystem *graphicsSystem,
                                          GameState *logicGameState,
                                          LogicSystem *logicSystem )
    {
		delete logicSystem;
        delete graphicsSystem;
        delete graphicsGameState;
    }

    const char* MainEntryPoints::getWindowTitle(void)
    {
		std::string windowTitle = "Space Ranchers ";
		windowTitle += SPACERANCHERS_VER;
		windowTitle += " - Dynasoul Studio";
		char* cstr = new char[windowTitle.length() + 1];
		std::strcpy(cstr, windowTitle.c_str());
        return cstr;
    }
}
The errors I've seen manifest in 3 ways, a quiet crash, a HEAP error, or this one:
Image

I'm sure the error generated is random and dependent on when one particular piece of code reaches some point.

I'm in need of some guidance with regards with some fundamental stuff.

Re: New to game programming - Can't setup Multi-Threading

Posted: Thu Aug 02, 2018 6:51 am
by dark_sylinc
I can't notice anything that would cause said crashes in the file you posted, it could be somewhere else.

Running the game within a debugger could give you a lot of data by retrieving a callstack on where it is crashing, or right before the exception is raised.

The only thing I could see is that you're leaking a char*:

Code: Select all

    const char* MainEntryPoints::getWindowTitle(void)
    {
		std::string windowTitle = "Space Ranchers ";
		windowTitle += SPACERANCHERS_VER;
		windowTitle += " - Dynasoul Studio";
		char* cstr = new char[windowTitle.length() + 1];
		std::strcpy(cstr, windowTitle.c_str());
        return cstr;
    }
That pointer will never be freed. Use static std::string windowTitle = "Space Ranchers "; and return windowTitle.c_str(); instead, or something like that (i.e. make std::string windowTitle a member variable and initialize it in the constructor).
However it doesn't look like this would be the cause of the crash.

Btw the Ogre.log should by default be generated in mWriteAccessFolder + "Ogre.log"

Cheers

Re: New to game programming - Can't setup Multi-Threading

Posted: Thu Aug 02, 2018 1:17 pm
by Dynasoul Studio
I've been combing through EmptyProject.cpp and carefully comparing it against the edits I've made and am starting to get a better feel for how the pointers variables interact with each other and learned a little more about how classes actually work. I was able to edit the background color properly by doing so.

Thanks for the tip regarding the leak, it was the only way I knew how to render the text in the title without it being a bunch of question marks in diamonds, that's so interesting:

Code: Select all

const char* MainEntryPoints::getWindowTitle(void)
    {
		static std::string windowTitle = "Space Ranchers ";
		windowTitle += SPACERANCHERS_VER;
		windowTitle += " - Dynasoul Studio";
		//char* cstr = new char[windowTitle.length() + 1];
		//std::strcpy(cstr, windowTitle.c_str());
        return windowTitle.c_str();
    }
Usually return windowTitle.c_str(); would return garbage but adding static to std::string windowTitle fixed it right up, and I was shocked when it didn't complain and ran because I was under the impression the static keyword was the same as const. (As in literally static, you can't change or add to something that's static - WRONG). In this case static just means the variable resides in a static location in memory as data.

Thanks for the help, I'll mess around with the debugger.