Page 1 of 8 123 ... LastLast
Results 1 to 10 of 80

Thread: Tutorial - Save System using DLLBind and c++

  1. #1
    Ayalaskin
    Guest

    Tutorial - Save System using DLLBind and c++

    Author: Renan Lopes (Ayalaskin).

    Save System using DLLBind and C++

    Programming skills required: Medium.

    I am a Brazilian, so I ask sorry about my English in advance.

    First of all, I would like to comment about my new blog, where I will keep track all my tutorials post that I make here in the Epics Forum.
    This is my third tutorial, but I am planning to do much more when I get some free time (that is rare lately).
    So check it if you want: Ayalas Tutorials.

    In this tutorial, we will build our own functional Save System, coding it inside a DLL using c++ then loading it in an UnrealScript class.
    I will use Visual Studio 2010 Beta version, but you can use any IDE/Compiler of your own choice.
    In the Appendix A I will cover some things about saving strings and in the Appendix B I will show how to setup Visual Studio 2010 to compile our dll, and how to set the paths so we don’t need to be copying/pasting files around.
    You can check some Epics thoughts about DLLBind here http://udn.epicgames.com/Three/DLLBind.htm.

    Note: If you use the Visual Studio 2010 to generate your dll, you will have to put the msvcr100.dll inside the (UDK)Binaries/Win32/UserCode.

    So let’s start programming the DLL, I will start without any strings, them I will add the support for them later, due some headaches it will cause.
    It’s is recommended that you are a least a medium programmer, since it’s not a trivial c++ code, and my intentions in this thread isn’t to teach basic programming.
    In this file we have two functions, one for saving the character properties, another to load it back.

    Note: Caution when copying and pasting, because the tag CODE in the forum mess with the character "", and shifts to the “” characters.
    SaveSystem.cpp
    Code:
    // This is the main DLL file.
    #include "stdafx.h"
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include "SaveSystem.h"
    
    // Declaring our character properties
    struct SCharacterProperties
    {
        int iGoldHeld;
        int iHealth;
        int iMana;
    };
    
    // This strucure will hold all our character substructures //
    struct SCharacterData
    {
        SCharacterProperties Properties;
        // We will add more structures inside here later //
    };
    
    // Telling that those functions will be used outside //
    extern "C"
    {
    	// We pass to this function the File name to save and the CharacterProperties structure to be saved //
       	__declspec(dllexport) bool SaveProperties(wchar_t* SaveName, SCharacterProperties* CharacterProperties)
    	{
    		FILE *pFile = 0;
    
                  // Creating the file for write into it //
    		_wfopen_s(&pFile, SaveName, L"w");
    		// Checking for error while creating the file //
    		if (!pFile)
    			return false;		
    
    		// Writing the properties //
    		fwrite(CharacterProperties, sizeof(SCharacterProperties), 1, pFile);	
    
    		// Closing the file //			
    		fclose(pFile);
    
                  // Everything Ok //	
    		return true;
    	}
    
    	__declspec(dllexport) SCharacterProperties* LoadProperties(wchar_t* SaveName)
    	{
    		// The object that will hold the loaded properties //
    		SCharacterProperties *CharacterProperties = new SCharacterProperties();						
    		FILE *pFile = 0;
    
    		// Opening the file to read from it//
    		_wfopen_s(&pFile, SaveName, L"r");
                  // Checking for error while loading the file //
    		if (!pFile)					
    			return NULL;		
    
    		// Loading properties //
    		fread(CharacterProperties, sizeof(SCharacterProperties), 1, pFile);		
                  // Closing the file //
    		fclose(pFile);
    
                  // Return the results //	
    		return CharacterProperties;
    	}
    }
    Note: The .dll and .obj file generated from compiling the above code should be put in the (UDK Folder)\Binaries\Win32\UserCode

    Now we have to make our UnrealScript class to recognize it.
    Create a file inside your game class folder.
    I will name it to SaveSystem.uc.

    SaveSystem.uc.
    Code:
    // Class definition, note that: DLLBind( // here goes your dll name //);
    class SaveSystem extends Object DLLBind(SaveSystem);
    
    // We have to define our structures here too //
    // Declaring our character properties
    struct SCharacterProperties
    {
        var int iGoldHeld;
        var int iHealth;
        var int iMana;
    };
    
    // This strucure will hold all our character substructures //
    struct SCharacterData
    {
        var SCharacterProperties Properties;
        // We will add more structures inside here later //
    };
    
    // Here we declare the functions we want to retrieve from the dll //
    // You can check the link I gave from epics, tocheck conversion types from c++ to UnrealScript
    dllimport final function bool SaveProperties(string SaveName, SCharacterProperties CharacterProperties);
    dllimport final function SCharacterProperties LoadProperties(string SaveName);
    
    // This will be called to save to the properties //
    function bool SaveTheCharacter(string SaveName, SCharacterData CharacterData)
    { 
        // We call the function we load from the dll, and pass the file name with the extension we desire for the properties //
        If (SaveProperties(SaveName @ “.pro”, CharacterData.CharacterProperties) == false)
            return false;
    
        return true;
    }
    
    function bool LoadTheCharacter(string SaveName, out SCharacterData CharacterData)
    {
        CharacterData.Properties = LoadCharacterProperties(SaveName @ “.pro”);
    
        return true;
    }
    Now, open your game info class, we will edit it to declare our save system, and make an interface, so we can call our save and load function via console command in the Unreal Editor via Kismet.

    GameInfo.uc
    Code:
    // Don’t forgot to tell that our game info class depends on our save system class //
    class GameInfo extends UTGame DependsOn(SaveSystem);
    // Declaring our save system variable //
    var SaveSystem MySaveSystem;
    // Declaring our player data variable
    var SCharacterData CharacterData;
    
    simulated function PostBeginPlay()
    {
        ///...///
        // Instantiating our saving system //
        MySaveSystem = new class'SaveSystem';
    }
    
    // This function have the keyword exec, telling that we can call if from console commands
    exec function SaveMyCharacter(string FileName)
    {
        // Calling the function inside our save system, and passing our character to be saved //
        MySaveSystem.SaveTheCharacter(FileName, CharacterData) ;
    }
    
    // Another exec function, now to load //
    exec function LoadMyCharacter(string FileName)
    {
        // Calling the function inside our save system, and passing our character that will hold the loaded data //
        MySaveSystem.LoadTheCharacter(FileName, CharacterData);
    }
    Compile the codes, everything should work, if I didn’t made any typos =P.

    Now let’s test our save system, open the unreal editor and place two trigger in the scenario, one trigger will execute the save and the other will execute the load.
    If you don’t know how to place a trigger check my blog and look for the Your Really first Game tutorial.

    Open the kismet, right click in a blank area and select New Action > Misc > Console Command,
    Click in the component you just created and adjust its attributes as shown:



    You can change the DesiredFileName to the file name you want.
    Now link the touch event of your trigger to the in slot of the console command, do that for the load function as well.



    Any troubles making this tutorial in your home, tell me then I will try to correct, I’ve tested everything, but it’s normal to make mistakes while converting the code to the tutorials, since we crop some functions and adjust things to be clearer.

    Check the Appendix A to see how we can save string.

    I will be updating this post several times to try to find any typos and errors in the codes.

    Note: All images I used here where Uploaded with ImageShack.us

  2. #2
    Ayalaskin
    Guest
    APPENDIX - A

    First of all, I have to thanks JackPorter for updating the dll section, helping us with the string inside a struct issue.

    Here we will implement a save system that also manages structs with strings inside it.
    There is still a limitation, our dll can't return structures with a string inside it, because when our dll finishes, it will clean the memory it allocated, and the unreal will also try to deallocate
    the memory, providing a crash.

    We have to manage the struct via an out parameter, and the limitation is, the string inside the structure passed in the out parameter, have to be initialized in the unrealscript, having
    the length greater than or equal the string we will load.
    So, i will declare a variable, that limits the string size, then we can safely load our strings, one time that we initialized all the string to the max size.
    Its not the best way to do it, we could save the strings lengths in a file, then load it back, and initialize all the strings in the right size to be loaded.

    SaveSystem.cpp
    Code:
    // This is the main DLL file.
    #include "stdafx.h"
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include "SaveSystem.h"
    
    // Unreal representation of an array //
    template<typename ArrayType>
    struct TArray
    {
        ArrayType* Data;
        int ArrayNum;
        int ArrayMax;
    };
    
    // Declaring our character properties
    struct SCharacterProperties
    {
        int iGoldHeld;
        int iHealth;
        int iMana;
    };
    
    // This strucure will hold all our character substructures //
    struct SCharacterData
    {
        SCharacterProperties Properties;
    
        // A string is an Array of wchar_t //
        TArray<wchar_t> PlayerName;
        TArray<wchar_t> PlayerClass;
    };
    
    // Telling that those functions will be used outside //
    extern "C"
    {
    	// We pass to this function the File name to save and the SCharacterData structure to be saved //
       	__declspec(dllexport) bool SaveCharacter(wchar_t* SaveName, SCharacterData* CharacterData)
    	{
    		FILE *pFile = 0;
    
                           // Creating the file for write into it //
    		_wfopen_s(&pFile, SaveName, L"w");
    		// Checking for error while creating the file //
    		if (!pFile)
    			return false;		
    
    		// Writing the properties //
    		fwrite(&CharacterData->Properties, sizeof(SCharacterProperties), 1, pFile);
    		// Writing the PlayerName ArrayNum //
    		fwrite(&CharacterData->PlayerName.ArrayNum, sizeof(int), 1, pFile);
    		 // Writing the PlayerName string //
    		fwrite(CharacterData->PlayerName.Data, sizeof(wchar_t), CharacterData->PlayerName.ArrayNum-1, pFile);
    		// Writing the PlayerClass ArrayNum //
    		fwrite(&CharacterData->PlayerClass.ArrayNum, sizeof(int), 1, pFile);
    		 // Writing the PlayerClass string //
    		fwrite(CharacterData->PlayerClass.Data, sizeof(wchar_t), CharacterData->PlayerClass.ArrayNum-1, pFile);
    
    		// Closing the file //			
    		fclose(pFile);
    
                          // Everything Ok //	
    		return true;
    	}
    
    	// We pass to this function the File name to load and the SCharacterData structure where the loaded data will reside //
       	__declspec(dllexport) bool LoadCharacter(wchar_t* SaveName, SCharacterData* CharacterData)
    	{
    		FILE *pFile = 0;
    
                           // Creating the file for load from it //
    		_wfopen_s(&pFile, SaveName, L"r");
    		// Checking for error while loading the file //
    		if (!pFile)
    			return false;		
    
    		// Loading the properties //
    		fread(&CharacterData->Properties, sizeof(SCharacterProperties), 1, pFile);
    		// Loading the PlayerName ArrayNum //
    		fread(&CharacterData->PlayerName.ArrayNum, sizeof(int), 1, pFile);
    		 // Loading the PlayerName string //
    		fread(CharacterData->PlayerName.Data, sizeof(wchar_t), CharacterData->PlayerName.ArrayNum-1, pFile);
    		// Loading the PlayerName ArrayNum //
    		fread(&CharacterData->PlayerClass.ArrayNum, sizeof(int), 1, pFile);
    		 // Loading the PlayerName string //
    		fread(CharacterData->PlayerClass.Data, sizeof(wchar_t), CharacterData->PlayerClass.ArrayNum-1, pFile);
    
    		CharacterData->PlayerClass.Data[CharacterData->PlayerClass.ArrayNum-1] = '\0';
    		CharacterData->PlayerClass.Data[CharacterData->PlayerClass.ArrayNum-1] = '\0';
    
    		// Closing the file //			
    		fclose(pFile);
    
                           // Everything Ok //	
    		return true;
    	}
    }
    Now see how our save system will be:

    SaveSystem.uc
    Code:
    // Class definition, note that: DLLBind( // here goes your dll name //);
    class SaveSystemString extends Object DLLBind(SaveSystem);
    
    // The max number of characters in one string
    var int MAX_STRING_LENGHT;
    
    // We have to define our structures here too //
    // Declaring our character properties
    struct SCharacterProperties
    {
        var int iGoldHeld;
        var int iHealth;
        var int iMana;
    };
    
    // This strucure will hold all our character substructures //
    struct SCharacterData
    {
        var SCharacterProperties Properties;
        var string PlayerName;
        var string PlayerClass;
    };
    
    // Here we declare the functions we want to retrieve from the dll //
    // You can check the link I gave from epics, tocheck conversion types from c++ to UnrealScript
    dllimport final function bool SaveCharacter(string SaveName, SCharacterData CharaterData);
    dllimport final function bool LoadCharacter(string SaveName, out SCharacterData CharaterData);
    
    dllimport final function TestCharacterData(SCharacterData Character);
    
    // The new implementation of the save //
    function bool SaveTheCharacter(string SaveName, SCharacterData CharacterData)
    {
        // Saving the properties //
       return SaveCharacter(SaveName, CharacterData);
    }
    
    // And the new implementation of the load function //
    function bool LoadTheCharacter(string SaveName, out SCharacterData CharacterData)
    {
        // Preparing the strings lenghts //
        PrepareData(CharacterData);
        // Loading the properties //
        return LoadCharacter(SaveName, CharacterData);
    }
    
    function PrepareData(out SCharacterData CharacterData)
    {
        // The unreal needs to allocate the memory for the strings
        // We have to pass the struct to the dll with the memory already allocated
        // So we define a max length to the strings
        // 15 should be enough to our case
        local int i;
        CharacterData.PlayerName = " ";
        CharacterData.PlayerClass = " ";
    
        for (i = 0; i < MAX_STRING_LENGHT-1; ++i)
        {
            CharacterData.PlayerName @= " ";
            CharacterData.PlayerClass @= " ";
        }
    }
    
    DefaultProperties
    {
        MAX_STRING_LENGHT = 16;
    }
    The GameInfo.uc should be the same.

    It's all for now.
    Check the Appendix B to see how to configure MSVC2010.

  3. #3
    Ayalaskin
    Guest
    APPENDIX - B

    Here I will show how to setup the visual studio 2010 to compile our dll.
    Open the Visual Studio 2010 and choose: File > New > Project.
    Then select Visual c++ > Class Library, and enter with your dll name.



    Now in the left side of the screen, in the solution explorer, right click your solution and choose Properties.



    My solution name is PMPSaveSystem, but if you fallowed the tutorial, yours will be just SaveSystem.

    In theproperties windows, in the combo box Configuration, select All Configurations,
    And in the Configuration Properties section Choose ‘General’, then in the OutPut Directory <Browse…> and find your ‘(UDK Folder)\Binaries\Win32\UserCode’ folder.



    Now select C/C++ then Code Generation in the Configuration Properties, and in the Struct Member Alignment choose 4 Bye (/Zp4).




    And done, you can compile and test your dlls now.

  4. #4
    ThePriest909
    Guest
    wow! My eternal thanks Ayalaskin!!!
    You've just saved us from a lot of troubleshooting.

  5. #5
    Allar
    Guest
    I've been struggling for days with strings within structs as well. No matter what I do it seems like the struct alignment is off or something, even with /Zp4 and the like. Glad I'm not the only one.

  6. #6
    Ayalaskin
    Guest
    Yeah, I lost like 5 days trying every kind of thing to avoid this bug/restriction, and no success, the default saving system via ini files, datastore doesn't make me happy.

  7. #7
    TheAgent
    Guest
    Im trying it out
    I keep getting this error

    Code:
    C:\UDK\In Its End\Development\Src\DSGame\Classes\SaveSystem.uc(42) : Error, 'SaveProperties' conflicts with 'Function DSGame.SaveSystem:SaveProperties'

  8. #8
    Ayalaskin
    Guest
    You just copied the code and pasted?
    It seems you have something with the same name as the function we implemented.

  9. #9
    TheAgent
    Guest
    I edited things, i think... i hit undo a couple times haha! maybe i undid something lemme post the code.

    I remember you had PMP character so i edited that, i forgot what else i edited i wasnt looking just wanted to try it out and test it hehe

    Code:
    /// Class definition, note that: DLLBind( // here goes your dll name //);
    class SaveSystem extends Object DLLBind(SaveSystem);
    
    // We have to define our structures here too //
    // Declaring our character properties
    struct SCharacterProperties
    {
        var int iGoldHeld;
        var int iHealth;
        var int iMana;
    };
    
    // This structure will hold the string lenghts 
    struct SCharacterLenghts
    {
        var int iPlayerFirstName;
        var int iPlayerMiddleName;
        var int iPlayerLastName;
    
    };
    
    // This structure will hold all the string
    struct SCharacterStrings
    {
        var string sPlayerFirstName;
        var string sPlayerMiddleName;
        var string sPlayerLastName;
    };
    
    // This strucure will hold all our character substructures //
    struct SCharacterData
    {
        var SCharacterProperties Properties;
        var SCharacterLenghts Lenghts;
        var SCharacterStrings Strings;	
    };
    
    // Here we declare the functions we want to retrieve from the dll //
    // You can check the link I gave from epics, tocheck conversion types from c++ to UnrealScript
    dllimport final function bool SaveProperties(string SaveName, SCharacterProperties CharacterProperties);
    dllimport final function  SCharacterProperties LoadProperties (string SaveName);
    dllimport final function bool SaveProperties(string SaveName, SCharacterProperties CharacterProperties);
    dllimport final function bool SaveLenghts(string SaveName, SCharacterLenghts CharacterLenghts);
    dllimport final function bool SaveString(string SaveName, string StringToSave, int Lenght);
    dllimport final function bool EraseStrings(string SaveName);
    
    // The new implementation of the save //
    function bool SaveTheCharacter(string SaveName, SCharacterData CharacterData)
    {
        // Saving the properties //
        if (SaveProperties(SaveName @ “.pro”, CharacterData.Properties) == false)
            return false;
    
        // Preparing the strings lenghts to be saved //
        CharacterData.Lenghts.iPlayerFirstName = len(CharacterData.Strings.sPlayerFisrtName);
        CharacterData.Lenghts.iPlayerMiddleName = len(CharacterData.Strings.sPlayerMiddleName);
        CharacterData.Lenghts.iPlayerLastName = len(CharacterData.Strings.sPlayerLastName);
    
        // Saving the lenghts //
        if (SaveLenghts(SaveName @ “.len”, CharacterData.Lenghts) == false)
            return false;
    
        // Erase the strings files before saving them //
        EraseStrings(SaveName @ “.str”);
    
        // Append all the strings //
        if (SaveString(SaveName @ “.str”, CharacterData.Strings.sPlayerFirstName, len(CharacterData.Strings.iPlayerFirstName)) == false)
            return false;
        if (SaveString(SaveName @ “.str”, CharacterData.Strings.sPlayerMiddleName, len(CharacterData.Strings.iPlayerMiddleName)) == false)
            return false;
        if (SaveString(SaveName @ “.str”, CharacterData.Strings.sPlayerLastName, len(CharacterData.Strings.iPlayerLastName)) == false)
            return false;
    
    }
    
    // And the new implementation of the load function //
    function bool LoadTheCharacter(string SaveName, out PMPCharacterData CharacterData)
    {
        // Loading the properties //
        CharacterData.Properties = LoadProperties(SaveName @ “.pro”);
        
        // Loading the string lenghts //
        CharacterData.Lenghts = LoadLenghts(SaveName @ “.len”);
    
        // Loading the strings, one by one //
        CharacterData.Strings.sPlayerFirstName = StaticLoadNextString(SaveName @ “.str”,  CharacterData.Lenghts.iPlayerFirstName, 0);
        CharacterData.Strings.sPlayerMiddleName = StaticLoadNextString(SaveName @ “.str”,  CharacterData.Lenghts.iPlayerMiddleName, 0);
        // Since this is the last string, we pass 1 as the last argument to tell the dll to close the file //
      CharacterData.Strings.sPlayerLastName = StaticLoadNextString(SaveName @ “.str”,  CharacterData.Lenghts.iPlayerLastName, 1);
    
        return true;
    }
    
    DefaultProperties
    {
    }

  10. #10
    Ayalaskin
    Guest
    In the dllimport, you have 2 functions with the same name, you have two SaveProperties functions, remove one.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •