skript-orm by heyhey123

Save player data with Skript syntax, without writing SQL.

Events

0

Expressions

0

Effects

0

Conditions

0

Types

0

Sections

0

Structures

0

Total

0

1.4.2 — Less work on the server thread 19 days, 8 hours and 18 minutes ago

  • Process owned local input and query results in the background while the script waits, reducing work on the server thread.
  • Spread item and location conversions across ticks, with a shared target budget of 2 ms per tick.
  • Fix SQLite binary handling and preserve unrelated local variables when replacing query results.

Large global results can still delay a tick. Prefer local result variables and smaller pages.

Updated benchmarks cover all five databases, local/global variables, and item/location data.

View Update

1.4.1: MariaDB and smoother bulk saves 20 days, 14 hours and 34 minutes ago

1.4.1 — includes the 1.4.0 changes not previously published here.

  • Dedicated MariaDB support.
  • Faster batch saving: read input across ticks and use parameterized multi-row MySQL inserts.
  • Reject reads above 5000 rows without partial output; split large writes within value and packet limits.
  • Fix table registration, NULL and type handling, local results, saved global lists and transaction cleanup.

View Update

v1.3.0 24 days, 10 hours and 53 minutes ago

1.3.0 update notes
Added

  • Raw statements: write the SQL or command yourself. execute query stores rows, execute update stores a count, and execute command is for MongoDB. The statement is sent as written — not parsed, not compared with a registered table, not translated between backends — which is what makes migrations, indexes and vendor-specific syntax expressible. Values are bound to ? placeholders by with; there is no other safety net. See Raw statements.

Fixed
  • A connection property nothing reads is now refused instead of dropped. A mistyped property did nothing, and database: on a MySQL connection reached a server with no default database and failed later, at an unrelated statement. It is now reported on the line that declares the connection, naming what the implementation does read.
  • A registration that no longer matches its table is now refused. A column added to a script never reached a table that already existed, so the first statement to use it failed with the backend's Unknown column .... Registration now compares columns, types, sizes, not null and the primary key, and reports on the declaration instead.

Changed
  • Startup reports which database implementations loaded, and names the reason for one that failed to load instead of passing in silence.

View Update

v1.2.0 34 days, 20 hours and 33 minutes ago

1.2.0 — a script can see more of what happened
Added: a write can report the rows it affected. End a statement with and store affected rows in {_rows}: 0 means it ran and matched nothing, while an unset variable means no statement answered — which is what makes a read-check-write safe without a transaction.
Changed: every statement now waits for its work, so the next line reads what the previous one wrote and a failure the database reported always reaches last database error. and wait is still accepted and does nothing, so no existing script has to change.
Fixed: a failure the database reports now really ends its transaction; a whole number that does not fit its column is refused instead of stored as a different number (5000000000 no longer becomes 705032704); MySQL's insert if absent no longer swallows errors other than a duplicate key; a refused read clears its result variable.

View Update

v1.1.0 35 days, 17 hours and 41 minutes ago

adds MongoDB, SQLite, PostgreSQL support:
write database "MongoDB" and the statements, types and limits behave the way they already do on MySQL. SQLite is documented as reachable through the generic "JDBC" type, PostgreSQL's tinyint columns read back as they should, and the jar no longer carries a database driver — Paper downloads the ones a bundled implementation needs on the server's first start.

View Update

skript-orm 36 days, 13 hours and 6 minutes ago



Store your Minecraft server's data with Skript. Define a table once, then save player records, rewards or logs using familiar Skript syntax. You do not need to learn SQL to get started.

Getting started · Examples · Benchmarks · Source code

Why use skript-orm?
  • Write scripts, not SQL strings. Read, insert, update and delete with dedicated syntax. Query results go straight into normal Skript variables.
  • Save Minecraft values directly. Column types include UUIDs, item stacks, locations, dates and timespans. Install SkBee to store NBT compounds too.
  • Keep player input separate from SQL. Structured statements bind values as parameters and check column names and value types. A player's name or message remains data. Handwritten raw statements need their own input precautions.
  • Save lists in batches. insert many processes owned local input in the background; global input is read in portions across ticks. Database work runs in the background. MySQL uses parameterized multi-row inserts.
  • Check that a save finished. The current trigger resumes after the operation completes, while the server can keep ticking. Check last database error before telling a player their data was saved.
  • Choose your database. MySQL, MariaDB, PostgreSQL, SQLite and MongoDB are covered by real database and Paper tests. Named connections let one script use several databases.


Install
Use Paper 26.2, Skript 2.16.2 and Java 25 or a compatible newer environment. SkBee is optional and only required for NBT compound columns.

  • Download the plugin jar from this resource or GitHub Releases.
  • Put it in plugins/ alongside Skript, then restart the server.
  • Create a script in plugins/Skript/scripts/, set your database credentials, and load it with /sk reload <script>.

Connections and tables are defined in your script. There is no separate addon configuration file to edit.

Which connection type should I use?
  • MySQL: "MySQL"; MariaDB: "MariaDB"; PostgreSQL: "PostgreSQL"; MongoDB: "MongoDB". These need a database server and account.
  • SQLite: "JDBC". It uses a local database file without a separate database service.

Paper supplies the MySQL and SQLite drivers and downloads the MariaDB, PostgreSQL and MongoDB drivers on first startup. MongoDB transactions are not exposed by this addon. See Compatibility for driver setup and database-specific limits.

Example: save and read a player record
This MySQL script keeps one row per player. Joining saves their current name and latest join date; /myrecord reads the saved data. Create the minecraft database and account first, then replace the example credentials.


on load:
set {playerdb::ready} to false
create a connection to database "MySQL" with properties:
url: "jdbc:mysql://localhost:3306/minecraft"
username: "minecraft"
password: "change-me"
if last database error is set:
send "Database connection failed: %last database error%" to console
stop

register a database table "players":
uuid: uuid, primary key, not null
name: string(64), not null
last_join: date, not null
if last database error is set:
send "Player table failed: %last database error%" to console
stop
set {playerdb::ready} to true

on join:
if {playerdb::ready} is not true:
stop
upsert one entity in table "players" by id uuid of player and wait:
values:
name: name of player
last_join: now
if last database error is set:
send "Could not save player data: %last database error%" to console

command /myrecord:
executable by: players
trigger:
if {playerdb::ready} is not true:
send "The database is not ready."
stop
select one entity from table "players" and store the result in {_row::*}:
where all:
uuid = uuid of player
if last database error is set:
send "Could not read your data. Please try again later."
send "Player lookup failed: %last database error%" to console
stop
if {_row::uuid} is not set:
send "No saved record yet."
stop
send "Saved name: %{_row::name}%"
send "Latest join date: %{_row::last_join}%"


by id means the table's primary key, uuid here. upsert creates a missing record or updates an existing one. A SQL date column stores the calendar date without the time of day. Table registration creates a missing table; it does not alter existing columns. See Tables before changing a deployed table.

Performance on a real server
Database calls run in the background. Lists in local variables such as {_rows::*} are also processed there while the current script waits; large global inputs are read in portions across ticks. Item and location conversions share a target budget of 2 ms per tick. A single slow conversion can exceed that target.

Run #7 read 5000 rows with six populated numeric columns on each supported database. The table compares local and global result variables for the same workload. Each value is a median of ten samples after three warmups.

[TABLE]
[TR][TH]Database[/TH][TH]Result variable[/TH][TH]Total time[/TH][TH]Server-thread processing time[/TH][/TR]
[TR][TD]SQLite[/TD][TD]Local[/TD][TD]49.925225 ms[/TD][TD]0.018713 ms[/TD][/TR]
[TR][TD]SQLite[/TD][TD]Global[/TD][TD]65.414039 ms[/TD][TD]15.483043 ms[/TD][/TR]
[TR][TD]MySQL[/TD][TD]Local[/TD][TD]49.923762 ms[/TD][TD]0.015720 ms[/TD][/TR]
[TR][TD]MySQL[/TD][TD]Global[/TD][TD]86.510248 ms[/TD][TD]36.381696 ms[/TD][/TR]
[TR][TD]MariaDB[/TD][TD]Local[/TD][TD]50.005949 ms[/TD][TD]0.017637 ms[/TD][/TR]
[TR][TD]MariaDB[/TD][TD]Global[/TD][TD]66.299079 ms[/TD][TD]15.888968 ms[/TD][/TR]
[TR][TD]PostgreSQL[/TD][TD]Local[/TD][TD]49.942947 ms[/TD][TD]0.018167 ms[/TD][/TR]
[TR][TD]PostgreSQL[/TD][TD]Global[/TD][TD]69.211086 ms[/TD][TD]19.242586 ms[/TD][/TR]
[TR][TD]MongoDB[/TD][TD]Local[/TD][TD]49.950872 ms[/TD][TD]0.014090 ms[/TD][/TR]
[TR][TD]MongoDB[/TD][TD]Global[/TD][TD]71.939851 ms[/TD][TD]21.958078 ms[/TD][/TR]
[/TABLE]

  • Database: the backend used for that row.
  • Result variable: whether the same query result is stored in local or global variables.
  • Total time: time from the read statement to script resumption, including background work and waiting.
  • Server-thread processing time: elapsed time in the plugin's recorded server-thread processing intervals. It is neither total server CPU time nor the complete tick duration.

The local path keeps ordinary result storage off the server thread. Complete reads still take about one tick because Skript resumes on the server thread. These figures compare variable scopes in the current implementation; they are not a before/after test or a performance comparison against another addon.

The run tested commit 76dcc7e on 2026-10-04 with Paper 26.2 build 124, Skript 2.16.2 and Java 25. Jobs used different CPUs, so the table does not rank databases. Large global object results can still delay a tick; prefer local results and smaller pages.

Timing uses System.nanoTime(); six-decimal milliseconds retain the recorded nanoseconds. See the benchmark guide for CPU models, write curves, item/location tests and raw reports.


Practical limits: A read returns at most 5000 rows; oversized results fail without partial output. Use pagination for larger datasets. Large writes are split into statements with at most 30000 bound values each. Keep the source variable unchanged until insert many finishes. Use a SQL transaction when several writes must succeed or roll back together.

How does it compare with skript-db?
The name skript-db covers several projects. The btk5h original and Limework fork focus on writing SQL queries. With skript-orm, you declare columns and use structured Skript operations instead. Minecraft types and query-result variables are built in, reducing the query construction and conversion code you maintain. The compared JDBC addons handle SQL databases; skript-orm also offers structured MongoDB operations.

Both the original and Limework projects provide SQL injection protection. skript-orm's structured statements bind values and also validate declared fields and value types. Safe raw queries still depend on how the script handles inputs. The 4w3 fork is maintained and provides parameter binding, batches and transactions too. Check the actual fork and its requirements before installing; the shared name does not mean every project has stopped development.

skript-orm processes owned local input in the background, reads global input across ticks, and uses parameterized multi-row MySQL inserts for batch saves. Batch behavior in skript-db depends on the fork, query and driver. We have no same-server performance comparison against these addons, so the results above do not establish a speed advantage over them. Choose skript-orm when you want storage to fit the Skript you already write; direct SQL can suit scripts that rely on complex joins or database-specific queries.

Documentation
Getting started · Writing rows · Reading rows · Types · Errors and waiting · Troubleshooting

Open source under the MIT license. Report an issue with your server, Skript and database versions, the failing script, and the relevant log.

View Update

© Copyright 2014-2026 skUnity

All rights reserved.