Jump to content

World::Save spits out empty file.


reepblue
 Share

Go to solution Solved by Josh,

Recommended Posts

Note: Scene::Save() works fine but doesn't copy over component data. 

The current map doesn't load any components in this test, but it should be worth testing.

#include "UltraEngine.h"
using namespace UltraEngine;

void main(const char* args, const int argc)
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_TITLEBAR | WINDOW_CENTER);

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(window);

    //Create a world
    auto world = CreateWorld();

    //Load a scene
    auto scene = LoadMap(world, "Maps/savetest.ultra");

    //Main loop
    while (!window->Closed() and !window->KeyHit(KEY_ESCAPE))
    {
        // Save 
        if (window->KeyHit(KEY_F5))
        {
            world->Save("save.ultra", SAVE_DEFAULT);
        }

        //Load
        if (window->KeyHit(KEY_F6))
        {
            scene = NULL;
            scene = LoadMap(world, "save.ultra");
        }

        //Update world
        world->Update();

        //Render world
        world->Render(framebuffer, true);
    }
}

 

savetest.zip

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

This method just creates a scene object and adds the world entities to it. However, the functionality to add all entities into a list of weak pointers was never added! The fastest fix is for me to just remove this method and the user can create a scene and add entities themselves.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I hope this will be demonstrated in the documentation. I was under the impression we would have "snapshot" saves by default with the world/scene::Save() function. 

It probably only worked with Lua components, but I'm looking forward to an example.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

Yeah, that's the idea. This appears to be working:

    auto p = CreatePivot(NULL);
    p->AddComponent<Mover>();

    auto map = std::make_shared<Scene>();
    map->AddEntity(p);

    map->Save("Maps/out.ultra");

Here is the output:
 

{
	"scene": {
		"base": [],
		"entities": [
			{
				"castShadows": false,
				"collisionType": 0,
				"extras": {
					"components": {
						"Mover": {
							"globalcoords": false,
							"movementspeed": [
								0,
								0,
								0
							],
							"rotationspeed": [
								0,
								0,
								0
							]
						}
					}
				},
				"pickMode": 0,
				"reflection": true,
				"uuid": "ffe028a9-4abe-49de-ba7b-30c2373cf196"
			}
		]
	}
}

 

  • Thanks 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Ok, so can just for loop all the entities within the world, and then add it to the scene? I'll give it a try and play around with it. My goal is to have Half Life 2 like save/load states almost transparent to the end user.

Might want to do this for every save call since things like bullets or instanced objects can spawn at any time.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

Regarding components. they don't seem to get updated upon a reload and I have no idea why. This save file has the Mover and a CameraControls component defined in the map file but upon a reload, it's like the components aren't actually being attached for some reason. Does the editor do something different? 

 

quicksave.zip

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

On 9/21/2023 at 5:25 PM, Josh said:

While this example works, it's not working in the way needed for game saves. The reload function throws an exception when trying to read a file and you get a bufferstream overflow with the example below. Also, since Reload doesn't create anything, would I need to load the map file before the save file? If so, what's the best way to know way to know what save file goes to which map?

#include "UltraEngine.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(window);

    //Create a scene
    auto scene = LoadMap(world, "Maps/savetest.ultra");

    //Save the starting scene to memory
    shared_ptr<BufferStream> stream = NULL;
    shared_ptr<BufferStream> binstream = NULL;

    //Main loop
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        if (window->KeyHit(KEY_F5))
        {
            Print("SAVE");
            if (stream) stream = NULL;
            if (binstream) binstream = NULL;
            stream = CreateBufferStream();
            binstream = CreateBufferStream();
            scene->Save(stream, binstream);
        }

        //Reload the starting scene when space key is pressed
        if (window->KeyHit(KEY_F6))
        {
            if (stream and binstream)
            {
                stream->Seek(0);
                binstream->Seek(0);
                scene->Reload(stream, binstream);
            }
        }

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

As for the save-to-file issue, I need to decide what I am trying to do here. My first implementation of the Ultra map format used an accompanying .bin file like glTF does, but after seeing how often people send glTF files and forget the .bin file I decided that was a bad idea.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Why don't you take a copy out of Valve's book and just make a map a package (zip) file. 

It might sound like the stupidest idea ever but hear me out:

  • You can keep your bin/json setup.
  • You can store baked cubemap files in there.
  • People can put in custom assets exclusive for that map.

I love the fact that map files are raw text. It makes it 100% easier to debug and find issues but we need binary and information available too. 

  • Upvote 1

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

The design I've been using for both maps and colliders is a JSON string, and then if there is any binary data, this is followed by a null character, and then the bin data. If there is no binary data then the file is just a normal JSON file.

This is very simple, and I figure some tool or plugin will arise that allows editing of the JSON text info without destroying the binary data. If the size of the JSON string changes, that's okay because all the offsets that point to the binary data are counted from the start of the data.

The Map:Reload() method is changed to only use one stream, and the example below is now using a file:
https://www.ultraengine.com/learn/Map_Reload?lang=cpp

The update is available now and I have tested it.

  • Thanks 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I got this error when using Map::Reload(). My map has a component for the player. This map has two components in it. 

image.thumb.png.e07fbbe62af86ca519b506a213b6441a.png

When using Map::LoadMap on the sav file, it works fine minus me forgetting to save a few values.

It looks like I'll need to do work on my end. I'm not 100% sure what Reload would do if the map is completely different. It seems safer to load the save file as a new map and trick the rest of my system to think you're playing the actual map file.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

4 minutes ago, reepblue said:

I got this error when using Map::Reload(). My map has a component for the player. This map has two components in it. 

image.thumb.png.e07fbbe62af86ca519b506a213b6441a.png

When using Map::LoadMap on the sav file, it works fine minus me forgetting to save a few values.

It looks like I'll need to do work on my end. I'm not 100% sure what Reload would do if the map is completely different. It seems safer to load the save file as a new map and trick the rest of my system to think you're playing the actual map file.

It searches for each entity by UUID and reloads its state. It's okay for there to be different entities or entities missing.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

41 minutes ago, Josh said:

It searches for each entity by UUID and reloads its state. It's okay for there to be different entities or entities missing.

Oh, so it's just a more efficient LoadMap so I can use this for any save files for any map at any time? 

You can get my seek error with this code and the map below. It has something to do with loading editor made maps.

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(window);

    auto scene = LoadMap(world, "Maps/savetest.ultra");

    ////Create a camera    
    //auto camera = CreateCamera(world);
    //camera->SetClearColor(0.125);
    //camera->SetPosition(0, 1, -4);
    //camera->AddComponent<Player>();

    ////Create light
    //auto light = CreateBoxLight(world);
    //light->SetRange(-10, 10);
    //light->SetArea(15, 15);
    //light->SetRotation(45, 35, 0);
    //light->SetColor(2);

    ////Create the ground
    //auto ground = CreateBox(world, 10, 1, 10);
    //ground->SetPosition(0, -0.5, 0);
    //ground->SetColor(0, 1, 0);

    ////Create a scene
    //auto scene = CreateMap();
    //scene->entities.push_back(ground);
    //scene->entities.push_back(light);
    //ground = NULL;
    //light = NULL;

    ////Add some boxes
    //for (int n = 0; n < 10; ++n)
    //{
    //    auto box = CreateBox(world);
    //    box->SetColor(0, 0, 1);
    //    box->SetPosition(Random(-5, 5), Random(5, 10), Random(-5, 5));
    //    box->SetMass(1);
    //    scene->entities.push_back(box);
    //}

    //Main loop
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {

        if (window->KeyHit(KEY_F5))
        {
            //Save the starting scene to a file
            scene->Save("game.sav");
        }

        //Reload the starting scene when space key is pressed
        if (window->KeyHit(KEY_F6))
        {
            scene->Reload("game.sav");
        }

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

savetest.zip

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

34 minutes ago, reepblue said:

Oh, so it's just a more efficient LoadMap so I can use this for any save files for any map at any time? 

Yes. The idea is to load the entity states instead of having to re-create the entire scene.

This can cause some restrictions on your code. Entities should not be created in code unless it is a result of some component code, and that component is responsible for saving and reloading any auxillary entity data. But if you follow the rules you get automatic quick/load states.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

15 minutes ago, Josh said:

Yes. The idea is to load the entity states instead of having to re-create the entire scene.

This can cause some restrictions on your code. Entities should not be created in code unless it is a result of some component code, and that component is responsible for saving and reloading any auxillary entity data. But if you follow the rules you get automatic quick/load states.

Ok, thanks. I can't easily test it until this works with map files but I'm assuming you just use scene->AddEntity() or scene->entities.push_back() in the Save() function for any entitles needed by the component. 

 

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

4 minutes ago, reepblue said:

Ok, thanks. I can't easily test it until this works with map files but I'm assuming you just use scene->AddEntity() or scene->entities.push_back() in the Save() function for any entitles needed by the component. 

If the entity creates a component, then it just needs to delete and re-create it in the Load method, or save the important properties into the saved JSON data.

If a component references another entity that another component owns, it can call GetUuid() to retrieve that entity's ID and store it in the JSON data.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

3 hours ago, Josh said:

If the entity creates a component, then it just needs to delete and re-create it in the Load method, or save the important properties into the saved JSON data.

If a component references another entity that another component owns, it can call GetUuid() to retrieve that entity's ID and store it in the JSON data.

I can confirm that it's working as intended. I think I need to rethink how to structure components as I'm having issues/conflicts.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

I think my problem is that Save/Load doesn't get called when Map::Reload() is used. Load gets called from LoadScene, however. 

#include "UltraEngine.h"
using namespace UltraEngine;

class Player : public Component
{
public:
    virtual void Start()
    {
        Print("Start()");
    }

    virtual bool Load(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const LoadFlags flags)
    {
        Print("Load()");
        return true;
    }

    virtual bool Save(table& properties, shared_ptr<Stream> binstream, shared_ptr<Map> scene, const SaveFlags flags)
    {
        Print("Save()");
        return true;
    }
};

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 1280, 720, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);

    //Create a world
    auto world = CreateWorld();

    //Create a framebuffer
    auto framebuffer = CreateFramebuffer(window);

    ////Create a camera    
    auto camera = CreateCamera(world);
    camera->SetClearColor(0.125);
    camera->SetPosition(0, 1, -4);
    camera->AddComponent<Player>();

    //Create light
    auto light = CreateBoxLight(world);
    light->SetRange(-10, 10);
    light->SetArea(15, 15);
    light->SetRotation(45, 35, 0);
    light->SetColor(2);

    //Create the ground
    auto ground = CreateBox(world, 10, 1, 10);
    ground->SetPosition(0, -0.5, 0);
    ground->SetColor(0, 1, 0);

    //Create a scene
    auto scene = CreateMap();
    scene->entities.push_back(ground);
    scene->entities.push_back(light);
    ground = NULL;
    light = NULL;

    //Add some boxes
    for (int n = 0; n < 10; ++n)
    {
        auto box = CreateBox(world);
        box->SetColor(0, 0, 1);
        box->SetPosition(Random(-5, 5), Random(5, 10), Random(-5, 5));
        box->SetMass(1);
        scene->entities.push_back(box);
    }

    //Main loop
    while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
    {
        if (window->KeyHit(KEY_F5))
        {
            //Save the starting scene to a file
            scene->Save("game.sav");
        }

        //Reload the starting scene when space key is pressed
        if (window->KeyHit(KEY_F6))
        {
            scene->Reload("game.sav");
        }

        world->Update();
        world->Render(framebuffer);
    }
    return 0;
}

 

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

  • Josh locked this topic
Guest
This topic is now closed to further replies.
 Share

×
×
  • Create New...