diff --git a/src/main/java/ch/njol/skript/bukkitutil/ServerUtils.java b/src/main/java/ch/njol/skript/bukkitutil/ServerUtils.java
new file mode 100644
index 00000000000..cb56f1ab5f1
--- /dev/null
+++ b/src/main/java/ch/njol/skript/bukkitutil/ServerUtils.java
@@ -0,0 +1,46 @@
+/**
+ * This file is part of Skript.
+ *
+ * Skript is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Skript is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Skript. If not, see .
+ *
+ * Copyright Peter Güttinger, SkriptLang team and contributors
+ */
+package ch.njol.skript.bukkitutil;
+
+import ch.njol.skript.Skript;
+import org.bukkit.Bukkit;
+import org.bukkit.ServerTickManager;
+
+public class ServerUtils {
+
+ private static final ServerTickManager SERVER_TICK_MANAGER;
+
+ static {
+ ServerTickManager serverTickManager = null;
+ if (Skript.methodExists(Bukkit.class, "getServerTickManager")) {
+ serverTickManager = Bukkit.getServerTickManager();
+ }
+ SERVER_TICK_MANAGER = serverTickManager;
+ }
+
+ public static ServerTickManager getServerTickManager() {
+ return SERVER_TICK_MANAGER;
+ }
+
+ public static boolean isServerTickManagerPresent() {
+ return SERVER_TICK_MANAGER != null;
+ }
+
+}
+
diff --git a/src/main/java/ch/njol/skript/conditions/CondIsTickFrozen.java b/src/main/java/ch/njol/skript/conditions/CondIsTickFrozen.java
new file mode 100644
index 00000000000..41064f8f939
--- /dev/null
+++ b/src/main/java/ch/njol/skript/conditions/CondIsTickFrozen.java
@@ -0,0 +1,56 @@
+/**
+ * This file is part of Skript.
+ *
+ * Skript is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Skript is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Skript. If not, see .
+ *
+ * Copyright Peter Güttinger, SkriptLang team and contributors
+ */
+package ch.njol.skript.conditions;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.conditions.base.PropertyCondition;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Entity;
+
+@Name("Is Entity Tick Frozen")
+@Description("Checks if the specified entities are frozen due to the server's ticking state.")
+@Examples("if target entity is tick frozen:")
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class CondIsTickFrozen extends PropertyCondition {
+
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ register(CondIsTickFrozen.class, "tick frozen", "entities");
+ }
+
+ @Override
+ public boolean check(Entity entity) {
+ return ServerUtils.getServerTickManager().isFrozen(entity);
+ }
+
+ @Override
+ protected String getPropertyName() {
+ return "tick frozen";
+ }
+
+}
+
diff --git a/src/main/java/ch/njol/skript/conditions/CondServerTickState.java b/src/main/java/ch/njol/skript/conditions/CondServerTickState.java
new file mode 100644
index 00000000000..cc41e829b20
--- /dev/null
+++ b/src/main/java/ch/njol/skript/conditions/CondServerTickState.java
@@ -0,0 +1,73 @@
+package ch.njol.skript.conditions;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Condition;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.util.Kleenean;
+import org.bukkit.ServerTickManager;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Locale;
+
+@Name("Server Tick State")
+@Description("Represents the ticking state of the server, for example, if the server is frozen, or running normally.")
+@Examples({
+ "if server's tick state is currently frozen:",
+ "if the server is sprinting:"
+})
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class CondServerTickState extends Condition {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerCondition(CondServerTickState.class,
+ "[the] server's tick[ing] state is [currently] (:frozen|:stepping|:sprinting|:normal)",
+ "[the] server's tick[ing] state (isn't|is not) [currently] (:frozen|:stepping|:sprinting|:normal)");
+ }
+
+ private ServerState state;
+
+ public enum ServerState {
+ FROZEN, STEPPING, SPRINTING, NORMAL
+ }
+
+ @Override
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ String tag = parseResult.tags.get(0).toUpperCase(Locale.ENGLISH);
+ state = ServerState.valueOf(tag);
+ setNegated(matchedPattern == 1);
+ return true;
+ }
+ @Override
+ public boolean check(Event event) {
+ ServerTickManager serverTickManager = ServerUtils.getServerTickManager();
+ boolean result = switch (state) {
+ case FROZEN -> serverTickManager.isFrozen();
+ case STEPPING -> serverTickManager.isStepping();
+ case SPRINTING -> serverTickManager.isSprinting();
+ case NORMAL -> serverTickManager.isRunningNormally();
+ };
+ return isNegated() != result;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ String stateStr;
+ if (isNegated()) {
+ stateStr = "the server's tick state isn't ";
+ } else {
+ stateStr = "the server's tick state is ";
+ }
+ return stateStr + state.toString().toLowerCase(Locale.ENGLISH);
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/effects/EffFreezeServer.java b/src/main/java/ch/njol/skript/effects/EffFreezeServer.java
new file mode 100644
index 00000000000..8713707b870
--- /dev/null
+++ b/src/main/java/ch/njol/skript/effects/EffFreezeServer.java
@@ -0,0 +1,52 @@
+package ch.njol.skript.effects;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Effect;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.util.Kleenean;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+@Name("Freeze/Unfreeze Server")
+@Description("Freezes or unfreezes the server.")
+@Examples({
+ "freeze server",
+ "unfreeze server"
+})
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class EffFreezeServer extends Effect {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerEffect(EffFreezeServer.class,
+ "freeze [the] server",
+ "unfreeze [the] server");
+ }
+
+ private boolean freeze;
+
+ @Override
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ freeze = matchedPattern == 0;
+ return true;
+ }
+
+ @Override
+ protected void execute(Event event) {
+ ServerUtils.getServerTickManager().setFrozen(freeze);
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return freeze ? "freeze server" : "unfreeze server";
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/effects/EffSprintServer.java b/src/main/java/ch/njol/skript/effects/EffSprintServer.java
new file mode 100644
index 00000000000..2a3a95164db
--- /dev/null
+++ b/src/main/java/ch/njol/skript/effects/EffSprintServer.java
@@ -0,0 +1,65 @@
+package ch.njol.skript.effects;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Effect;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.util.Timespan;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.util.Kleenean;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+@Name("Sprint Server")
+@Description({
+ "Makes the server sprint for a certain amount of time, or stops the server from sprinting.",
+ "Sprinting is where the server increases the tick rate depending on the time you input, and resets the tick rate to what it was after the server has finished sprinting."
+})
+@Examples({
+ "request server to sprint for 10 seconds",
+ "make server stop sprinting"
+})
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class EffSprintServer extends Effect {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerEffect(EffSprintServer.class,
+ "make [the] server sprint for %timespan%",
+ "make [the] server stop sprinting");
+ }
+
+ private Expression timespan;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ if (matchedPattern == 0)
+ timespan = (Expression) exprs[0];
+ return true;
+ }
+
+ @Override
+ protected void execute(Event event) {
+ if (timespan != null) {
+ long sprintTicks = timespan.getOptionalSingle(event).map(Timespan::getTicks).orElse(1L);
+ ServerUtils.getServerTickManager().requestGameToSprint((int) sprintTicks);
+ } else {
+ ServerUtils.getServerTickManager().stopSprinting();
+ }
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ if (timespan != null)
+ return "make the server sprint for " + timespan.toString(event, debug);
+ return "make the server stop sprinting";
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/effects/EffStepServer.java b/src/main/java/ch/njol/skript/effects/EffStepServer.java
new file mode 100644
index 00000000000..df5cd8f641e
--- /dev/null
+++ b/src/main/java/ch/njol/skript/effects/EffStepServer.java
@@ -0,0 +1,66 @@
+package ch.njol.skript.effects;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Effect;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.util.Timespan;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.util.Kleenean;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+@Name("Step Server")
+@Description({
+ "Makes the server \"step\" for a certain amount of time",
+ "The server can only step when its ticking state is frozen.",
+ "When stepping, the server goes forward that amount of time in ticks."
+})
+@Examples({
+ "make server step for 5 seconds",
+ "make server stop stepping"
+})
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class EffStepServer extends Effect {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerEffect(EffStepServer.class,
+ "make [the] server step for %timespan%",
+ "make [the] server stop stepping");
+ }
+
+ private Expression timespan;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ if (matchedPattern == 0)
+ timespan = (Expression) exprs[0];
+ return true;
+ }
+
+ @Override
+ protected void execute(Event event) {
+ if (timespan != null) {
+ Timespan timespan = this.timespan.getSingle(event);
+ if (timespan != null)
+ ServerUtils.getServerTickManager().stepGameIfFrozen((int) timespan.getAs(Timespan.TimePeriod.TICKS));
+ } else {
+ ServerUtils.getServerTickManager().stopStepping();
+ }
+ }
+
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return timespan == null ? "make the server stop stepping" : "make the server step for " + timespan.toString(event, debug);
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprFrozenTicksToRun.java b/src/main/java/ch/njol/skript/expressions/ExprFrozenTicksToRun.java
new file mode 100644
index 00000000000..cee4884d2c2
--- /dev/null
+++ b/src/main/java/ch/njol/skript/expressions/ExprFrozenTicksToRun.java
@@ -0,0 +1,59 @@
+package ch.njol.skript.expressions;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.ExpressionType;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.lang.util.SimpleExpression;
+import ch.njol.util.Kleenean;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+@Name("Frozen Ticks to Run")
+@Description("Gets the amount of ticks that are in queue to run while the server's tick state is frozen.")
+@Examples("broadcast \"There are %frozen ticks to run% frozen ticks left!\"")
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class ExprFrozenTicksToRun extends SimpleExpression {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerExpression(ExprFrozenTicksToRun.class, Integer.class, ExpressionType.SIMPLE, "[the] [amount of] frozen ticks [left] to run");
+ }
+
+ @Override
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ return true;
+ }
+
+ @Override
+ protected Integer @Nullable [] get(Event event) {
+ int frozenTicks = ServerUtils.getServerTickManager().getFrozenTicksToRun();
+ if (frozenTicks > 0) {
+ return new Integer[]{frozenTicks};
+ }
+ return new Integer[0];
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public Class extends Integer> getReturnType() {
+ return Integer.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return "the frozen ticks left to run";
+ }
+
+}
diff --git a/src/main/java/ch/njol/skript/expressions/ExprServerTickRate.java b/src/main/java/ch/njol/skript/expressions/ExprServerTickRate.java
new file mode 100644
index 00000000000..de426fdc333
--- /dev/null
+++ b/src/main/java/ch/njol/skript/expressions/ExprServerTickRate.java
@@ -0,0 +1,100 @@
+package ch.njol.skript.expressions;
+
+import ch.njol.skript.Skript;
+import ch.njol.skript.bukkitutil.ServerUtils;
+import ch.njol.skript.classes.Changer.ChangeMode;
+import ch.njol.skript.doc.Description;
+import ch.njol.skript.doc.Examples;
+import ch.njol.skript.doc.Name;
+import ch.njol.skript.doc.RequiredPlugins;
+import ch.njol.skript.doc.Since;
+import ch.njol.skript.lang.Expression;
+import ch.njol.skript.lang.ExpressionType;
+import ch.njol.skript.lang.SkriptParser.ParseResult;
+import ch.njol.skript.lang.util.SimpleExpression;
+import ch.njol.util.Kleenean;
+import ch.njol.util.Math2;
+import ch.njol.util.coll.CollectionUtils;
+import org.bukkit.event.Event;
+import org.jetbrains.annotations.Nullable;
+
+@Name("Server Tick Rate")
+@Description({
+ "Gets or sets the current tick rate of the server. The tick rate is the number of game ticks that occur in a second. Higher values mean the game runs faster.",
+ "The server's default tick rate is 20."
+})
+@Examples({
+ "send \"%server's tick rate%\" to player",
+ "set server's tick rate to 20 # This is the default tick rate.",
+ "add 5 to server's tick rate",
+ "remove 2 from server's tick rate"
+})
+@Since("INSERT VERSION")
+@RequiredPlugins("Minecraft 1.20.4+")
+public class ExprServerTickRate extends SimpleExpression {
+
+ static {
+ if (ServerUtils.isServerTickManagerPresent())
+ Skript.registerExpression(ExprServerTickRate.class, Float.class, ExpressionType.SIMPLE, "[the] server['s] tick rate");
+ }
+
+ @Override
+ public boolean init(Expression>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ protected Float[] get(Event event) {
+ return new Float[] {ServerUtils.getServerTickManager().getTickRate()};
+ }
+
+ public Class>[] acceptChange(ChangeMode mode) {
+ switch (mode) {
+ case SET, ADD, REMOVE, RESET -> return CollectionUtils.array(Number.class);
+ default -> return null;
+ }
+ }
+
+ @Override
+ public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
+ float tickRate = ServerUtils.getServerTickManager().getTickRate();
+ float change = delta != null ? ((Number) delta[0]).floatValue() : 0;
+ float newTickRate = tickRate;
+
+ switch (mode) {
+ case SET:
+ newTickRate = change;
+ break;
+ case ADD:
+ newTickRate = tickRate + change;
+ break;
+ case REMOVE:
+ newTickRate = tickRate - change;
+ break;
+ case RESET:
+ newTickRate = 20;
+ break;
+ }
+
+ newTickRate = Math2.fit(newTickRate, 1.0f, 10000f);
+
+ ServerUtils.getServerTickManager().setTickRate(newTickRate);
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public Class extends Float> getReturnType() {
+ return Float.class;
+ }
+
+ @Override
+ public String toString(@Nullable Event event, boolean debug) {
+ return "the server's tick rate";
+ }
+
+}
diff --git a/src/test/skript/tests/syntaxes/conditions/CondServerTickState.sk b/src/test/skript/tests/syntaxes/conditions/CondServerTickState.sk
new file mode 100644
index 00000000000..4a34b54bb8a
--- /dev/null
+++ b/src/test/skript/tests/syntaxes/conditions/CondServerTickState.sk
@@ -0,0 +1,26 @@
+test "Server Tick State Expressions" when running minecraft "1.20.4":
+ # Test if server is normal
+ assert server's tick state is normal with "Server tick state is not normal"
+
+ freeze the server
+ assert server's tick state is frozen with "Server tick state is not frozen"
+
+ # Step the server and test
+ make the server step for 5 seconds
+ assert server's tick state is stepping with "Server tick state is not stepping"
+ make the server stop stepping
+ unfreeze the server
+
+ # Sprint the server and test
+ make the server sprint for 10 seconds
+ assert server's tick state is sprinting with "Server tick state is not sprinting"
+ make the server stop sprinting
+
+test "Entity Tick Frozen Expression" when running minecraft "1.20.4":
+ spawn a cow at spawn of world "world"
+ freeze the server
+ wait 1 tick
+ assert last spawned cow is tick frozen with "Entity is not tick frozen"
+ unfreeze the server
+ assert last spawned cow is not tick frozen with "Entity is still tick frozen after unfreezing"
+
diff --git a/src/test/skript/tests/syntaxes/effects/EffServerTick.sk b/src/test/skript/tests/syntaxes/effects/EffServerTick.sk
new file mode 100644
index 00000000000..33c8af2c9e7
--- /dev/null
+++ b/src/test/skript/tests/syntaxes/effects/EffServerTick.sk
@@ -0,0 +1,51 @@
+test "Server Sprinting Effect" when running minecraft "1.20.4":
+ make the server sprint for 10 seconds
+ assert server's tick state is sprinting with "Server did not start sprinting"
+
+ make the server stop sprinting
+ assert server's tick state is normal with "Server did not stop sprinting"
+
+test "Server Stepping Effect" when running minecraft "1.20.4":
+ make the server step for 5 seconds
+ assert server's tick state is stepping with "Server did not start stepping"
+
+ make the server stop stepping
+ assert server's tick state is normal with "Server did not stop stepping"
+
+test "Server Freezing Effect" when running minecraft "1.20.4":
+ freeze the server
+ assert server's tick state is frozen with "Server did not freeze"
+
+ unfreeze the server
+ assert server's tick state is normal with "Server did not unfreeze"
+
+test "Server Tick Rate Expression" when running minecraft "1.20.4":
+ # Ensure default tick rate is 20
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 20 with "Default server tick rate is not 20"
+
+ set server's tick rate to 25
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 25 with "Server tick rate was not set to 25"
+
+ add 5 to server's tick rate
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 30 with "Server tick rate was not increased to 30"
+
+ remove 10 from server's tick rate
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 20 with "Server tick rate was not decreased to 20"
+
+ set server's tick rate to -5
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 1 with "Setting negative tick rate did not clamp."
+
+ add -3 to server's tick rate
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 1 with "Adding a negative value clamped the tick rate."
+
+ # Reset the server tick rate to default and verify
+ reset server's tick rate
+ set {_tickRate} to server's tick rate
+ assert {_tickRate} is 20 with "Server tick rate was not reset to 20"
+