Apply suggestions from code review

Co-Authored-By: Tulio Leao <tupaschoal@gmail.com>
Co-Authored-By: Michael Steenbeek <m.o.steenbeek@gmail.com>
This commit is contained in:
Ted John 2020-04-22 16:06:29 +01:00
parent efbb385bf7
commit d480fb8daa
24 changed files with 55 additions and 60 deletions

View File

@ -2,7 +2,7 @@
OpenRCT2 allows custom scripts (also known as plug-ins) to be written and executed in the game providing additional behaviour on top of the vanilla experience. This can range from extra windows providing information about the park to entire new multiplayer game modes.
Each script is a single physical javascript file within the `plugin` directory in your OpenRCT2 user directory. This is usually `C:\Users\YourName\Documents\OpenRCT2`, or `/home/YourName/.config/openrct2` on Linux. OpenRCT2 will load every single file with the extension `.js` in this directory recursively. So if you want to prevent a plug-in from being used, you must move it outside this directory, or rename it so the filename does not end with `.js`.
Each script is a single physical javascript file within the `plugin` directory in your OpenRCT2 user directory. This is usually `C:\Users\YourName\Documents\OpenRCT2` on Windows, or `/home/YourName/.config/openrct2` on Linux. OpenRCT2 will load every single file with the extension `.js` in this directory recursively. So if you want to prevent a plug-in from being used, you must move it outside this directory, or rename it so the filename does not end with `.js`.
There are two types of scripts:
* Local
@ -10,16 +10,16 @@ There are two types of scripts:
Local scripts can **not** alter the game state. This allows each player to enable any local script for their own game without other players needing to also enable the same script. These scripts tend to provide extra tools for productivity, or new windows containing information.
Remote scripts on the other hand can alter the game state in certain contexts thus must be enabled for every player in a multiplayer game. Players can not enable or disable remote scripts for multiplayer servers they join. Instead the server will upload any remote scripts that have been enabled on the server to each player. This allows servers to enable scripts without players needing to manually download or enable the same script on their end.
Remote scripts on the other hand can alter the game state in certain contexts, thus must be enabled for every player in a multiplayer game. Players **cannot** enable or disable remote scripts for multiplayer servers they join. Instead the server will upload any remote scripts that have been enabled on the server to each player. This allows servers to enable scripts without players needing to manually download or enable the same script on their end.
## Writing Scripts
Scripts are written in ECMAScript 5 compatible JavaScript. OpenRCT2 currently uses the [duktape](https://duktape.org) library to execute scripts. This however does not mean you need to write your plug-in in JavaScript, there are many transpilers that allow you to write in a language of your choice and then compile it to JavaScript allowing it to be executed by OpenRCT2. JavaScript or [TypeScript](https://www.typescriptlang.org) is recommended however, as that will allow you to utilise the type definition file we supply (`openrct2.d.ts`). If you would like to use ECMAScript 6 or later which contain features such as the `let` keyword or classes, then you will need to use a transpiler such as [Babel](https://babeljs.io) or [TypeScript](https://www.typescriptlang.org).
Offical references for writing plug-ins are:
Official references for writing plug-ins are:
* The API: `openrct2.d.ts` distributed with OpenRCT2.
* Our collection of sample scripts: https://github.com/OpenRCT2/plugin-samples
* A TypeScript plug-in comprised on multiple sources: https://github.com/IntelOrca/OpenRCT2-ParkManager
* Our collection of sample scripts: [OpenRCT2/plugin-samples](https://github.com/OpenRCT2/plugin-samples)
* A TypeScript plug-in comprised of multiple sources: [IntelOrca/OpenRCT2-ParkManager](https://github.com/IntelOrca/OpenRCT2-ParkManager)
Start by copying this template script into a new file in your `plugin` directory:
```js
@ -36,23 +36,23 @@ registerPlugin({
});
```
This will log a message to stdout when you open any park. If you are on Windows, make sure to run `openrct2.com` instead of `openrct2.exe` so you can interact with the stdin / stdout console. The console is a JavaScript interpreter (REPL), this means you can write and test expressions similar to the console found in web browsers when you press F12. When you make changes to your script, you must exit your current game and open it again for the script to reload... unless you use the hot reload feature.
This will log a message to the terminal screen (`stdout`) when you open any park. If you are on Windows, make sure to run `openrct2.com` instead of `openrct2.exe` so you can interact with the `stdin` / `stdout` console. The console is a JavaScript interpreter (REPL), this means you can write and test expressions similar to the console found in web browsers when you press `F12`. When you make changes to your script, you must exit your current game and open it again for the script to reload... unless you use the hot reload feature.
The hot reload feature can be enabled by editing your `config.ini` file and setting `enable_hot_reloading` to `true` under `[plugin]`. When this is enabled, the game will auto-reload the script in real-time whenever you save your JavaScript file. This is allows rapid development of plug-ins. You may even want to write temporary code to preview your changes quickly, such as closing and opening a specific custom window on startup. A demonstration of this can be found here: https://www.youtube.com/watch?v=jmjWzEhmDjk
The hot reload feature can be enabled by editing your `config.ini` file and setting `enable_hot_reloading` to `true` under `[plugin]`. When this is enabled, the game will auto-reload the script in real-time whenever you save your JavaScript file. This allows rapid development of plug-ins as you can write code and quickly preview your changes, such as closing and opening a specific custom window on startup. A demonstration of this can be found on YouTube: [OpenRCT2 plugin hot-reload demo](https://www.youtube.com/watch?v=jmjWzEhmDjk)
## Frequently Asked Questions
> Why was JavaScript chosen instead LUA or Python.
> Why was JavaScript chosen instead of LUA or Python.
JavaScript is a very mature and flexible language with a large, if not largest, resource base available. There are arguably more transpilers, tools, and libraries for JavaScript than any other language. That and also it using a familiar C-like syntax and 0-indexed arrays make it more suitable than LUA and Python. Of course if you would still like to use LUA or another language, there will likely be a JavaScript transpiler for it.
Another benefit of using JavaScript is that you get rich editor features such as completion and API documentation by using a TypeScript definition file which works for both TypeScript and JavaScript. [Visual Studio Code](https://code.visualstudio.com) is recommended, as that supports the workflow very well. See this video for a demonstration of the editor functionality: https://www.youtube.com/watch?v=jmjWzEhmDjk
Another benefit of using JavaScript is that you get rich editor features such as completion and API documentation by using a TypeScript definition file which works for both TypeScript and JavaScript. [Visual Studio Code](https://code.visualstudio.com) is recommended, as that supports the workflow very well. See the [OpenRCT2 plugin hot-reload demo](https://www.youtube.com/watch?v=jmjWzEhmDjk) video for a demonstration of the editor functionality.
> How safe are scripts?
Scripts are executed in a sandbox container with no direct access to your computer. Scripts can only use the APIs we provide which only consist of interactions to OpenRCT2 and a limited API for storing data. It is technically possible for a script to freeze the game, or fill your disc up with data, but these aren't particularly severe issues and are noticed quite quickly.
The [duktape](https://duktape.org) library is used to execute scripts, it is a very mature library for executing scripts, but no library can promise 100% security. If any security vulnerabilities are found, they will likely be fixed promptly and OpenRCT2 can then be updated to use the new version of duktape.
The [duktape](https://duktape.org) library is used to execute scripts, it is a very mature library for executing scripts, but no library can promise 100% security. If any security vulnerabilities are found, they will likely be fixed promptly and OpenRCT2 can then be updated to use the new version of `duktape`.
> Is it possible for someone to run a bitcoin miner in my game?
@ -62,19 +62,19 @@ Yes, but the performance would be so dire that it would be a waste of their time
Scripts can conist of any behaviour and have a large memory pool available to them. The speed will vary depending on the hardware and system executing them. The scripts are interpreted, so do not expect anywhere close to the performance of native code. In most scenarios this should be satisfactory, but a random map generator, or genetic algorithm for building roller coasters might struggle. Like any language, there will be tricks to optimising JavaScript and the use of the OpenRCT2 APIs.
The APIs for OpenRCT2 try to mimic the internal data structures as close as possible but we can only add so many at a time. The best way to grow the plug-in system is to add APIs on-demand. So if you find an API is missing, please raise an issue for it on GitHub.
The APIs for OpenRCT2 try to mimic the internal data structures as close as possible but we can only add so many at a time. The best way to grow the plug-in system is to add APIs on-demand. So if you find an API is missing, please raise an issue for it on GitHub and also feel free to submit a pull request afterwards.
> How do I debug my script?
Debugging has not yet been implemented but is planned. In the meantime, you can use `console.log` to print useful information for diagnosing problems.
Debugging has not yet been implemented, but is planned. In the meantime, you can use `console.log` to print useful information for diagnosing problems.
> What does the error 'Game state is not mutable in this context' mean?
This means you are attempting to modify the game state (e.g. change the park, map or guests etc.) in a context where you should not be doing so. This might be because your script is defined as `local` meaning it must work independently of other players not having the script enabled, or a remote script attempting to modify the game in the main function or a user interface event.
This means you are attempting to modify the game state (e.g. change the park, map or guests etc.) in a context where you should not be doing so. This might be because your script is defined as `local`, meaning it must work independently of other players, not having the script enabled, or a remote script attempting to modify the game in the main function or a user interface event.
Any changes to the game state must be synchronised across all players so that the same changes happen on the same tick for every player. This prevents the game going out of sync. To do this you must only change game state in a compatible hook such as `interval.day` or in the execute method of a game action. Game actions allow players to make specific changes to the game providing they have the correct permissions and the server allows it.
Whilst OpenRCT2 try's to prevent desynchronision from happening, it still requires careful coding to ensure the behaviour is deterministic across all clients. Any attempt to use local specific or non-deterministic data to change the game state will cause a desync. This can be as easy as using `ui.windows`, a variable that can be different for every player.
Whilst OpenRCT2 tries to prevent desynchronisation from happening, it still requires careful coding to ensure the behaviour is deterministic across all clients. Any attempt to use local specific or non-deterministic data to change the game state will cause a desync. This can be as easy as using `ui.windows`, a variable that can be different for every player.
> What are hooks?
@ -100,7 +100,7 @@ Game actions allow you to define new actions (with a new permission slot) that p
6. Send action to all player to execute action at tick ###.
7. All players execute action at tick ###.
This sequence of actions ensure that every player execute the action exactly the same way on the exact same game tick. It also allows the server to validate that the action is allowed and does not fail due to permission or another player executing conflicting action just before it. This is why there is a noticable delay when constructing in multiplayer games, as the client has to wait for the server to acknowledge the action and reply with the tick number to execute it on, since we do not yet have any rollback support.
This sequence of actions ensures that every player execute the action exactly in the same way on the exact same game tick. It also allows the server to validate that the action is allowed and does not fail due to permission or another player executing a conflicting action just before it. This is why there is a noticeable delay when constructing in multiplayer games, as the client has to wait for the server to acknowledge the action and reply with the tick number to execute it on, since we do not yet have any rollback support.
> Can I run code specifically for servers or clients?
@ -139,7 +139,7 @@ Yes, remote scripts are uploaded to every client and run as-is. Even if the serv
> Can plugins persist data across multiple OpenRCT2 instances?
Yes, use `context.sharedStorage` to read or write data to an external JSON file: `plugin.store.json` in the OpenRCT2 suer directory. It is recommended that you namespace all your plugin's data under a common name.
Yes, use `context.sharedStorage` to read or write data to an external JSON file: `plugin.store.json` in the OpenRCT2 user directory. It is recommended that you namespace all your plugin's data under a common name.
```js
var h = context.sharedStorage.get('IntelOrca.MagicLift.Height');
@ -156,7 +156,7 @@ Absolutely, just embed the library in your JavaScript file. There are a number o
> Can I share code across multiple scripts?
There are two ways this can be done. If you are just sharing helper routines or libraries, the best thing is to embed it in each plug-in. There are a number of tools for JavaScript to help you do this. If you would like two plug-ins to communicate with each other, perhaps to share data, then you can do this by declaring a variable or function in global scope which is available to all plug-ins, or use the shared storage APIs.
Yes, and there are two ways this can be done. If you are just sharing helper routines or libraries, the best thing is to embed it in each plug-in. There are a number of tools for JavaScript to help you do this. If you would like two plug-ins to communicate with each other, perhaps to share data, then you can do this by declaring a variable or function in global scope which is available to all plug-ins, or use the shared storage APIs.
> What do I do if there is no API for ...?
@ -170,8 +170,8 @@ We recommend [GitHub](https://github.com) (where OpenRCT2 is hosted), or another
> What licence should I use for my script?
This is up to you. The OpenRCT2 licence does not enforce any licence requirement for content that is loaded into it. MIT is recommended as is the most permissive. You also have to consider what licence any third party libraries you are using allow.
This is up to you. The OpenRCT2 licence does not enforce any licence requirement for content that is loaded into it. MIT is recommended as it is the most permissive. You also have to consider what licence any third party libraries you are using allow.
> Is there a good place to distribute my script to other players?
There is currently no official database for this. For now the recommendation is to upload releases for your script on GitHub alongside your source code (if public). Some people like to make a GitHub repository that just consists of a list of content (scripts in this case) which anyone can add to via pull requests.
There is currently no official database for this. For now the recommendation is to upload releases of your script on GitHub alongside your source code (if public). Some people like to make a GitHub repository that just consists of a list of content (scripts in this case) which anyone can add to via pull requests.

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2020 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2020 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
@ -27,7 +27,7 @@ namespace OpenRCT2::Scripting
std::string Text;
DukValue Callback;
CustomToolbarMenuItem(std::shared_ptr<Plugin> owner, std::string text, DukValue callback)
CustomToolbarMenuItem(std::shared_ptr<Plugin> owner, const std::string& text, DukValue callback)
: Owner(owner)
, Text(text)
, Callback(callback)

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -52,30 +52,30 @@ GameActionResult::GameActionResult(GA_ERROR error, rct_string_id title, rct_stri
std::string GameActionResult::GetErrorTitle() const
{
std::string titlez;
if (auto title = ErrorTitle.AsString())
std::string title;
if (auto error = ErrorTitle.AsString())
{
titlez = *title;
title = *error;
}
else
{
titlez = format_string(ErrorTitle.GetStringId(), nullptr);
title = format_string(ErrorTitle.GetStringId(), nullptr);
}
return titlez;
return title;
}
std::string GameActionResult::GetErrorMessage() const
{
std::string messagez;
if (auto message = ErrorMessage.AsString())
std::string message;
if (auto error = ErrorMessage.AsString())
{
messagez = *message;
message = *error;
}
else
{
messagez = format_string(ErrorMessage.GetStringId(), ErrorMessageArgs.data());
message = format_string(ErrorMessage.GetStringId(), ErrorMessageArgs.data());
}
return messagez;
return message;
}
namespace GameActions

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
@ -80,9 +80,7 @@ namespace OpenRCT2::Scripting
return _hasStarted;
}
Plugin()
{
}
Plugin() = default;
Plugin(duk_context* context, const std::string& path);
Plugin(const Plugin&) = delete;
Plugin(Plugin&&) = delete;

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2020 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
@ -21,14 +21,11 @@ namespace OpenRCT2::Scripting
class ScConfiguration
{
private:
bool _isUserConfig{};
bool _isUserConfig{ true };
DukValue _backingObject;
public:
ScConfiguration()
: _isUserConfig(true)
{
}
ScConfiguration() = default;
ScConfiguration(const DukValue& backingObject)
: _backingObject(backingObject)

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2020 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Copyright (c) 2014-2018 OpenRCT2 developers
* Copyright (c) 2014-2020 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2