A short solution to prevent the game from restarting(Not commerce)

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
Andredron
Miko-Class Veteran
Posts: 700
Joined: Thu Dec 28, 2017 2:37 pm
Location: Russia
Contact:

A short solution to prevent the game from restarting(Not commerce)

#1 Post by Andredron »

The information was taken from here

https://www.renpy.cn/thread-222-1-7.html

https://github.com/SecondThundeR/DokiDoki-RenPy

Decompiled sources are provided only for personal use, file analysis in order to gain experience in creating games on Ren'Py and creating modifications for DDLC. Creation of autonomous projects using DDLC sources is prohibited by Team Salvato

Frederisk:
You may have discovered that multiple games written by Ren'Py can run simultaneously during debugging, and even the Ren'Py engine itself can run multiple games at the same time.

Of course, this won't be a big problem under normal circumstances, but more professional users will realize that it's easy to cause file overwriting or even thread conflicts, for example, two windows choose to save to the same file at the same time. The location of the file or blocking of the same stream resource will not only worsen the gameplay, but, more seriously, will lead to the fact that the game will be insufficient, and even the game's own file will be damaged.

Unfortunately, so far, Ren'Py officials have not provided the appropriate attribute variables or methods that could be directly invoked to prevent this situation. But the good news is that Python can achieve this, and it's relatively easy. This method requires an external reference file (of course, it can also be embedded): singleton.py, which contains several classes and fields, specific content can be found in the content, and I have added a lot of comments to simplify it. people who don't have a deep understanding of Python to read. It can be seen that this solution determines the current status of the game by creating a temporary file. The implementation is somewhat similar to C# mutual exclusion, but it is based not on streams, but on files.



https://github.com/SecondThundeR/DokiDo ... ngleton.py

Code: Select all

#! /usr/bin/env python

import sys
import os
import tempfile
import logging


class SingleInstanceException(BaseException):
    pass


class SingleInstance:

    """
    If you want to prevent your script from running in parallel just instantiate SingleInstance() class. If is there another instance already running it will throw a `SingleInstanceException`.
    >>> import tendo
    ... me = SingleInstance()
    This option is very useful if you have scripts executed by crontab at small amounts of time.
    Remember that this works by creating a lock file with a filename based on the full path to the script file.
    Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create multiple singleton instances from the same file. This is particularly useful if you want specific functions to have their own singleton instances.
    """

    def __init__(self, flavor_id=""):
        self.initialized = False
        basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace(
            "/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock'
        # os.path.splitext(os.path.abspath(sys.modules['__main__'].__file__))[0].replace("/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock'
        self.lockfile = os.path.normpath(
            tempfile.gettempdir() + '/' + basename)

        logger.debug("SingleInstance lockfile: " + self.lockfile)
        if sys.platform == 'win32':
            try:
                # file already exists, we try to remove (in case previous
                # execution was interrupted)
                if os.path.exists(self.lockfile):
                    os.unlink(self.lockfile)
                self.fd = os.open(
                    self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR)
            except OSError:
                x, e, tb = sys.exc_info()
                if e.errno == 13:
                    logger.error(
                        "Another instance is already running, quitting.")
                    raise SingleInstanceException()
                print(e.errno)
                raise
        else:  # non Windows
            import fcntl
            self.fp = open(self.lockfile, 'w')
            self.fp.flush()
            try:
                fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
            except IOError:
                logger.warning(
                    "Another instance is already running, quitting.")
                raise SingleInstanceException()
        self.initialized = True

    def __del__(self):
        if not self.initialized:
            return
        try:
            if sys.platform == 'win32':
                if hasattr(self, 'fd'):
                    os.close(self.fd)
                    os.unlink(self.lockfile)
            else:
                import fcntl
                fcntl.lockf(self.fp, fcntl.LOCK_UN)
                # os.close(self.fp)
                if os.path.isfile(self.lockfile):
                    os.unlink(self.lockfile)
        except Exception as e:
            if logger:
                logger.warning(e)
            else:
                print("Unloggable error: %s" % e)
            sys.exit(-1)


def f(name):
    tmp = logger.level
    logger.setLevel(logging.CRITICAL)  # we do not want to see the warning
    try:
        me2 = SingleInstance(flavor_id=name)  # noqa
    except SingleInstanceException:
        sys.exit(-1)
    logger.setLevel(tmp)

logger = logging.getLogger("tendo.singleton")
logger.addHandler(logging.StreamHandler())



The method of use is also very simple, since Ren'Py provides that all external dependent code files must be placed in the /game/python-packages/ directory, first create this folder, then unzip the above code files and put them in It all, here is a special reminder that, although the file name is singleton, it has nothing to do with the singleton provided in pip, so don't try to use pip to extract the file.

The code is also very easy to use, just create an instance of SingleInstance.

Code: Select all

python early:
    import singleton
    me = singleton.SingleInstance()

It is recommended to write this code in /game/definitions.rpy.

When the game opens more, the process throws a SingleInstanceException error due to a write conflict and completes the operation. If you enable console output during debugging, you will be able to get detailed information generated by this error.

This method is used in DDLC (Heartbeat Literary Society!), and the file can also be found in its /game/python-packages/ directory.

User avatar
PyTom
Ren'Py Creator
Posts: 16088
Joined: Mon Feb 02, 2004 10:58 am
Completed: Moonlight Walks
Projects: Ren'Py
IRC Nick: renpytom
Github: renpytom
itch: renpytom
Location: Kings Park, NY
Contact:

Re: A short solution to prevent the game from restarting(Not commerce)

#2 Post by PyTom »

The premise of this is wrong. Ren'Py is designed merge together save files and persistent data from multiple running instances. While this is mostly intended for save-sync, it should also work locally.
Supporting creators since 2004
(When was the last time you backed up your game?)
"Do good work." - Virgil Ivan "Gus" Grissom
Software > Drama • https://www.patreon.com/renpytom

Post Reply

Who is online

Users browsing this forum: Majestic-12 [Bot]