Merge pull request #1 from MeexReay/main

Main Pull request
This commit is contained in:
MeexReay 2024-04-04 13:53:11 +03:00 committed by GitHub
commit cd42d8e5b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1696 additions and 300 deletions

121
LICENSE
View File

@ -1,121 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -1,9 +1,17 @@
# Fabric Example Mod # Repeating Mod (Fabric)
## Setup This mod can record your movements and play them back.
For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using. ![Preview gif](preview.gif)
## License ## Controls
This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects. ```
Menu | J
Toggle recording | not specified
Toggle replay | not specified
```
## Menu
![Repeating menu](https://github.com/MeexReay/repeating-mod/assets/127148610/da923fe5-d44d-421b-b601-2a65cb5543eb)

View File

@ -13,9 +13,17 @@ repositories {
// See https://docs.gradle.org/current/userguide/declaring_repositories.html // See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories. // for more information about repositories.
maven { url 'https://maven.wispforest.io' } maven { url 'https://maven.wispforest.io' }
mavenCentral()
} }
dependencies { dependencies {
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
//add joml
modImplementation 'org.joml:joml:1.10.4'
include 'org.joml:joml:1.10.4'
// To change the versions see the gradle.properties file // To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
@ -29,15 +37,13 @@ dependencies {
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
modImplementation "io.wispforest:owo-lib:${project.owo_version}" // modImplementation "io.wispforest:owo-lib:${project.owo_version}"
// only if you plan to use owo-config // // only if you plan to use owo-config
annotationProcessor "io.wispforest:owo-lib:${project.owo_version}" // annotationProcessor "io.wispforest:owo-lib:${project.owo_version}"
// include this if you don't want force your users to install owo // include this if you don't want force your users to install owo
// sentinel will warn them and give the option to download it automatically // sentinel will warn them and give the option to download it automatically
include "io.wispforest:owo-sentinel:${project.owo_version}" // include "io.wispforest:owo-sentinel:${project.owo_version}"
// https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple
implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'
} }
base { base {

View File

@ -4,16 +4,16 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
minecraft_version=1.19.3 minecraft_version=1.20
yarn_mappings=1.19.3+build.1 yarn_mappings=1.20+build.1
loader_version=0.14.17 loader_version=0.14.23
# Mod Properties # Mod Properties
mod_version = 1.0.0 mod_version = 1.0.7
maven_group = themixray.repeating.mod maven_group = themixray.repeating.mod
archives_base_name = repeating-mod archives_base_name = repeating-mod
# Dependencies # Dependencies
fabric_version=0.76.1+1.19.3 fabric_version=0.83.0+1.20
owo_version=0.10.3+1.19.3 #owo_version=0.11.1+1.20

BIN
preview.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

View File

@ -0,0 +1,103 @@
package themixray.repeating.mod;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
public class EasyConfig {
public final Path path;
public final File file;
public Map<String,String> data;
public EasyConfig(File f, Map<String,String> def) {
this.path = f.toPath();
this.file = f;
this.data = new HashMap<>();
if (!file.exists()) {
try {
file.createNewFile();
write(def);
} catch (IOException e) {
e.printStackTrace();
}
}
reload();
for (Map.Entry<String,String> m:def.entrySet())
if (!data.containsKey(m.getKey()))
data.put(m.getKey(),m.getValue());
save();
}
public EasyConfig(Path f, Map<String,String> def) {
this(f.toFile(),def);
}
public EasyConfig(String parent,String child,Map<String,String> def) {
this(new File(parent,child),def);
}
public EasyConfig(File parent,String child,Map<String,String> def) {
this(new File(parent,child),def);
}
public EasyConfig(Path parent,String child,Map<String,String> def) {
this(new File(parent.toFile(),child),def);
}
public EasyConfig(File f) {
this(f,new HashMap<>());
}
public EasyConfig(Path path) {
this(path.toFile(),new HashMap<>());
}
public EasyConfig(String parent,String child) {
this(new File(parent,child),new HashMap<>());
}
public EasyConfig(File parent,String child) {
this(new File(parent,child),new HashMap<>());
}
public EasyConfig(Path parent,String child) {
this(new File(parent.toFile(),child),new HashMap<>());
}
public void reload() {
data = read();
}
public void save() {
write(data);
}
private String toText(Map<String,String> p) {
StringBuilder t = new StringBuilder();
for (Map.Entry<String,String> e:p.entrySet())
t.append(e.getKey()).append("=").append(e.getValue()).append("\n");
return t.toString();
}
private Map<String,String> toMap(String j) {
Map<String,String> m = new HashMap<>();
for (String l:j.split("\n")) {
String s[] = l.split("=");
m.put(s[0],s[1]);
}
return m;
}
private Map<String,String> read() {
try {
return toMap(Files.readString(path));
} catch (IOException e) {
e.printStackTrace();
}
return new HashMap<>();
}
private void write(Map<String,String> p) {
try {
Files.write(path, toText(p).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -4,20 +4,22 @@ import com.google.common.collect.Lists;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.client.rendering.v1.DimensionRenderingRegistry;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.render.*;
import net.minecraft.client.util.InputUtil; import net.minecraft.client.util.InputUtil;
import net.minecraft.entity.MovementType; import net.minecraft.entity.MovementType;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.resource.Resource; import net.minecraft.registry.RegistryKey;
import net.minecraft.resource.ResourceManager; import net.minecraft.registry.RegistryKeys;
import net.minecraft.resource.ResourceType; import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
@ -25,12 +27,16 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import themixray.repeating.mod.render.RenderHelper;
import themixray.repeating.mod.render.RenderSystem;
import themixray.repeating.mod.render.buffer.WorldBuffer;
import java.io.File; import java.awt.*;
import java.io.InputStream;
import java.util.*; import java.util.*;
import java.util.List;
public class RepeatingMod implements ClientModInitializer { public class RepeatingMod implements ClientModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod"); public static final Logger LOGGER = LoggerFactory.getLogger("repeating-mod");
@ -38,22 +44,29 @@ public class RepeatingMod implements ClientModInitializer {
public static final FabricLoader loader = FabricLoader.getInstance(); public static final FabricLoader loader = FabricLoader.getInstance();
public static RepeatingMod me; public static RepeatingMod me;
public Vec3d start_record_pos = null;
public Vec3d finish_record_pos = null;
public List<RecordEvent> record = new ArrayList<>(); public List<RecordEvent> record = new ArrayList<>();
public boolean is_recording = false; public boolean is_recording = false;
public Date last_record = null; public long last_record = -1;
public TickTask move_tick = null;
public Thread replay = null; public TickTask replay_tick = null;
public boolean is_replaying = false; public boolean is_replaying = false;
public boolean loop_replay = false; public boolean loop_replay = false;
public static boolean replay_sneaking = false; public static RecordInputEvent input_replay = null;
public long living_ticks = 0;
public static RepeatingScreen menu; public static RepeatingScreen menu;
private static KeyBinding menu_key; private static KeyBinding menu_key;
private static KeyBinding toggle_replay_key; private static KeyBinding toggle_replay_key;
private static KeyBinding toggle_record_key; private static KeyBinding toggle_record_key;
public double record_blocks_limit = 2; public long record_pos_delay = 20;
public long record_time_limit = 50;
public static Random rand = new Random();
public EasyConfig conf; public EasyConfig conf;
@ -62,13 +75,56 @@ public class RepeatingMod implements ClientModInitializer {
LOGGER.info("Repeating mod initialized"); LOGGER.info("Repeating mod initialized");
me = this; me = this;
Map<String,Object> def = new HashMap<>(); RenderSystem.init();
def.put("record_blocks_limit", record_blocks_limit); WorldRenderEvents.LAST.register(context -> {
def.put("record_time_limit", record_time_limit); WorldBuffer buffer = RenderHelper.startTri(context);
conf = new EasyConfig(new File(loader.getConfigDir().toFile(),"repeating-mod").toPath(),def); if (start_record_pos != null) {
RenderHelper.drawRectFromTri(buffer,
(float) start_record_pos.getX() - 0.25F,
(float) start_record_pos.getY() + 0.01F,
(float) start_record_pos.getZ() - 0.25F,
record_blocks_limit = (double) conf.data.get("record_blocks_limit"); (float) start_record_pos.getX() + 0.25F,
record_time_limit = (long) conf.data.get("record_time_limit"); (float) start_record_pos.getY() + 0.01F,
(float) start_record_pos.getZ() - 0.25F,
(float) start_record_pos.getX() + 0.25F,
(float) start_record_pos.getY() + 0.01F,
(float) start_record_pos.getZ() + 0.25F,
(float) start_record_pos.getX() - 0.25F,
(float) start_record_pos.getY() + 0.01F,
(float) start_record_pos.getZ() + 0.25F,
new Color(70,230,70,128));
}
if (finish_record_pos != null) {
RenderHelper.drawRectFromTri(buffer,
(float) finish_record_pos.getX() - 0.25F,
(float) finish_record_pos.getY() + 0.01F,
(float) finish_record_pos.getZ() - 0.25F,
(float) finish_record_pos.getX() + 0.25F,
(float) finish_record_pos.getY() + 0.01F,
(float) finish_record_pos.getZ() - 0.25F,
(float) finish_record_pos.getX() + 0.25F,
(float) finish_record_pos.getY() + 0.01F,
(float) finish_record_pos.getZ() + 0.25F,
(float) finish_record_pos.getX() - 0.25F,
(float) finish_record_pos.getY() + 0.01F,
(float) finish_record_pos.getZ() + 0.25F,
new Color(230,70,70,128));
}
RenderHelper.endTri(buffer);
});
Map<String,String> def = new HashMap<>();
def.put("record_pos_delay", String.valueOf(record_pos_delay));
conf = new EasyConfig(loader.getConfigDir(),"repeating-mod",def);
record_pos_delay = Long.parseLong(conf.data.get("record_pos_delay"));
menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding( menu_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.repeating-mod.menu",InputUtil.Type.KEYSYM, "key.repeating-mod.menu",InputUtil.Type.KEYSYM,
@ -82,9 +138,8 @@ public class RepeatingMod implements ClientModInitializer {
menu = new RepeatingScreen(); menu = new RepeatingScreen();
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (menu_key.wasPressed()) { if (menu_key.wasPressed())
client.setScreen(menu); client.setScreen(menu);
}
if (toggle_replay_key.wasPressed()) { if (toggle_replay_key.wasPressed()) {
if (!is_recording) { if (!is_recording) {
if (is_replaying) if (is_replaying)
@ -102,6 +157,13 @@ public class RepeatingMod implements ClientModInitializer {
} }
} }
}); });
new TickTask(0,0) {
@Override
public void run() {
living_ticks++;
}
};
} }
public RecordEvent getLastRecord(String t) { public RecordEvent getLastRecord(String t) {
@ -113,28 +175,131 @@ public class RepeatingMod implements ClientModInitializer {
return null; return null;
} }
public void startRecording() { public void startRecording() {
is_recording = true; is_recording = true;
menu.update_btns(); menu.update_btns();
record.clear(); record.clear();
finish_record_pos = null;
Vec3d v = client.player.getPos();
record.add(new RecordMoveEvent(v,client.player.getHeadYaw(),client.player.getPitch()));
start_record_pos = v;
if (record_pos_delay > 0) {
move_tick = new TickTask(
record_pos_delay,
record_pos_delay) {
@Override
public void run() {
record.add(new RecordMoveEvent(client.player.getPos(),
client.player.getHeadYaw(), client.player.getPitch()));
}
};
}
sendMessage(Text.translatable("message.repeating-mod.record_start")); sendMessage(Text.translatable("message.repeating-mod.record_start"));
} }
public void recordTick(RecordEvent e) { public void recordTick(RecordEvent e) {
Date now = new Date(); if (is_recording) {
if (last_record != null) { long now = living_ticks;
long diff = now.getTime() - last_record.getTime(); if (last_record != -1) {
if (diff >= 0) record.add(new RecordDelayEvent(diff)); long diff = now - last_record - 2;
if (diff > 0) record.add(new RecordDelayEvent(diff));
}
record.add(e);
last_record = now;
}
}
public void recordAllInput() {
RecordInputEvent l = ((RecordInputEvent)getLastRecord("input"));
if (l == null) {
RecordInputEvent e = new RecordInputEvent(
client.player.input.sneaking,
client.player.input.jumping,
client.player.input.movementSideways,
client.player.input.movementForward,
client.player.input.pressingForward,
client.player.input.pressingBack,
client.player.input.pressingLeft,
client.player.input.pressingRight,
client.player.getHeadYaw(),
client.player.getBodyYaw(),
client.player.getPitch(),
client.player.isSprinting(),
client.player.getYaw(),
client.player.getMovementSpeed());
recordTick(e);
} else {
RecordInputEvent e = new RecordInputEvent(
((Boolean) client.player.input.sneaking == l.sneaking) ? null : client.player.input.sneaking,
((Boolean) client.player.input.jumping == l.jumping) ? null : client.player.input.jumping,
(((Float) client.player.input.movementSideways).equals(l.movementSideways)) ? null : client.player.input.movementSideways,
(((Float) client.player.input.movementForward).equals(l.movementForward)) ? null : client.player.input.movementForward,
((Boolean) client.player.input.pressingForward == l.pressingForward) ? null : client.player.input.pressingForward,
((Boolean) client.player.input.pressingBack == l.pressingBack) ? null : client.player.input.pressingBack,
((Boolean) client.player.input.pressingLeft == l.pressingLeft) ? null : client.player.input.pressingLeft,
((Boolean) client.player.input.pressingRight == l.pressingRight) ? null : client.player.input.pressingRight,
client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(),
((Boolean) client.player.isSprinting() == l.sprinting) ? null : client.player.isSprinting(),
client.player.getYaw(),client.player.getMovementSpeed());
if (!(e.isEmpty() &&
e.yaw == l.yaw &&
e.head_yaw == l.head_yaw &&
e.pitch == l.pitch &&
e.body_yaw == l.body_yaw)) {
e.fillEmpty(l);
recordTick(e);
}
}
}
public void recordCameraInput() {
RecordInputEvent l = ((RecordInputEvent)getLastRecord("input"));
if (l == null) {
RecordInputEvent e = new RecordInputEvent(
client.player.input.sneaking,
client.player.input.jumping,
client.player.input.movementSideways,
client.player.input.movementForward,
client.player.input.pressingForward,
client.player.input.pressingBack,
client.player.input.pressingLeft,
client.player.input.pressingRight,
client.player.getHeadYaw(),
client.player.getBodyYaw(),
client.player.getPitch(),
client.player.isSprinting(),
client.player.getYaw(),
client.player.getMovementSpeed());
recordTick(e);
} else {
RecordInputEvent e = new RecordInputEvent(null,null,null,
null,null,null,null,null,
client.player.getHeadYaw(),RepeatingMod.client.player.getBodyYaw(),client.player.getPitch(),
null,client.player.getYaw(),client.player.getMovementSpeed());
if (!(e.yaw == l.yaw &&
e.head_yaw == l.head_yaw &&
e.pitch == l.pitch &&
e.body_yaw == l.body_yaw)) {
e.fillEmpty(l);
recordTick(e);
}
} }
record.add(e);
last_record = now;
} }
public void stopRecording() { public void stopRecording() {
is_recording = false; is_recording = false;
finish_record_pos = client.player.getPos();
if (move_tick != null) {
move_tick.cancel();
move_tick = null;
}
menu.update_btns(); menu.update_btns();
last_record = null; last_record = -1;
sendMessage(Text.translatable("message.repeating-mod.record_stop")); sendMessage(Text.translatable("message.repeating-mod.record_stop"));
} }
@ -143,25 +308,42 @@ public class RepeatingMod implements ClientModInitializer {
is_recording = false; is_recording = false;
is_replaying = true; is_replaying = true;
menu.update_btns(); menu.update_btns();
client.player.setNoGravity(true); replay_tick = new TickTask(0,0, TickTask.TickAt.CLIENT_TAIL) {
replay = new Thread(() -> { public int replay_index = 0;
while (true) {
for (RecordEvent e : record) @Override
if (is_replaying) public void run() {
e.callback(); if (!is_replaying) cancel();
if (!loop_replay || !is_replaying) break; RecordEvent e = record.get(replay_index);
if (e instanceof RecordDelayEvent) {
setDelay(((RecordDelayEvent) e).delay);
} else {
e.callback();
}
replay_index++;
if (!loop_replay) {
if (replay_index == record.size()) {
stopReplay();
cancel();
}
} else if (replay_index == record.size()) {
replay_index = 0;
}
} }
stopReplay(); };
});
replay.start();
sendMessage(Text.translatable("message.repeating-mod.replay_start")); sendMessage(Text.translatable("message.repeating-mod.replay_start"));
} }
public void stopReplay() { public void stopReplay() {
is_recording = false; is_recording = false;
is_replaying = false; is_replaying = false;
if (replay_tick != null) {
replay_tick.cancel();
replay_tick = null;
}
menu.update_btns(); menu.update_btns();
client.player.setNoGravity(false);
sendMessage(Text.translatable("message.repeating-mod.replay_stop")); sendMessage(Text.translatable("message.repeating-mod.replay_stop"));
} }
@ -171,10 +353,15 @@ public class RepeatingMod implements ClientModInitializer {
return (double) Math.round(value * factor) / factor; return (double) Math.round(value * factor) / factor;
} }
public static void sendMessage(Text text) { public static void sendMessage(MutableText text) {
client.player.sendMessage(Text.literal("[") client.player.sendMessage(Text.literal("[")
.append(Text.translatable("text.repeating-mod.name")) .append(Text.translatable("text.repeating-mod.name"))
.append("] ").append(text)); .append("] ").formatted(Formatting.BOLD,Formatting.DARK_GRAY)
.append(text.formatted(Formatting.RESET).formatted(Formatting.GRAY)));
}
public static void sendDebug(String s) {
client.player.sendMessage(Text.literal("[DEBUG] ").append(Text.of(s)));
} }
public static abstract class RecordEvent { public static abstract class RecordEvent {
@ -187,36 +374,15 @@ public class RepeatingMod implements ClientModInitializer {
String type = String.valueOf(t.charAt(0)); String type = String.valueOf(t.charAt(0));
String[] args = t.substring(2).split("&"); String[] args = t.substring(2).split("&");
if (type.equals("d")) { if (type.equals("d")) {
return new RecordDelayEvent( return RecordDelayEvent.fromArgs(args);
Long.parseLong(args[0]));
} else if (type.equals("m")) { } else if (type.equals("m")) {
return new RecordMoveEvent(new Vec3d( return RecordMoveEvent.fromArgs(args);
Double.parseDouble(args[0]), } else if (type.equals("p")) {
Double.parseDouble(args[1]), return RecordInputEvent.fromArgs(args);
Double.parseDouble(args[2])),
Float.parseFloat(args[3]),
Float.parseFloat(args[4]));
} else if (type.equals("s")) {
return new RecordSneakEvent(
args[0].equals("1"));
} else if (type.equals("b")) { } else if (type.equals("b")) {
return new RecordBlockBreakEvent(new BlockPos( return RecordBlockBreakEvent.fromArgs(args);
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2])));
} else if (type.equals("i")) { } else if (type.equals("i")) {
return new RecordBlockInteractEvent( return RecordBlockInteractEvent.fromArgs(args);
Hand.valueOf(args[5]),
new BlockHitResult(new Vec3d(
Double.parseDouble(args[0]),
Double.parseDouble(args[1]),
Double.parseDouble(args[2])),
Direction.byId(Integer.parseInt(args[4])),
new BlockPos(
Integer.parseInt(args[0]),
Integer.parseInt(args[1]),
Integer.parseInt(args[2])),
args[3].equals("1")));
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -228,13 +394,17 @@ public class RepeatingMod implements ClientModInitializer {
public static class RecordDelayEvent extends RecordEvent { public static class RecordDelayEvent extends RecordEvent {
public long delay; public long delay;
public static RecordDelayEvent fromArgs(String[] a) {
return new RecordDelayEvent(Long.parseLong(a[0]));
}
public RecordDelayEvent(long delay) { public RecordDelayEvent(long delay) {
this.delay = delay; this.delay = delay;
} }
public void callback() { public void callback() {
try { try {
Thread.sleep(delay); Thread.sleep(delay/20*1000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -253,6 +423,15 @@ public class RepeatingMod implements ClientModInitializer {
public float yaw; public float yaw;
public float pitch; public float pitch;
public static RecordMoveEvent fromArgs(String[] a) {
return new RecordMoveEvent(new Vec3d(
Double.parseDouble(a[0]),
Double.parseDouble(a[1]),
Double.parseDouble(a[2])),
Float.parseFloat(a[3]),
Float.parseFloat(a[4]));
}
public RecordMoveEvent(Vec3d vec,float yaw,float pitch) { public RecordMoveEvent(Vec3d vec,float yaw,float pitch) {
this.vec = vec; this.vec = vec;
this.yaw = yaw; this.yaw = yaw;
@ -275,28 +454,160 @@ public class RepeatingMod implements ClientModInitializer {
} }
} }
public static class RecordSneakEvent extends RecordEvent { public static class RecordInputEvent extends RecordEvent {
public boolean sneaking; public Boolean sneaking;
public Boolean jumping;
public Boolean pressingForward;
public Boolean pressingBack;
public Boolean pressingLeft;
public Boolean pressingRight;
public Boolean sprinting;
public RecordSneakEvent(boolean sneaking) { public Float movementSideways;
public Float movementForward;
public float yaw;
public float head_yaw;
public float body_yaw;
public float pitch;
public float speed;
public static RecordInputEvent fromArgs(String[] a) {
return new RecordInputEvent(
(a[0].equals("n")?null:a[0].equals("1")),
(a[1].equals("n")?null:a[1].equals("1")),
(a[2].equals("n")?null:Float.parseFloat(a[2])),
(a[3].equals("n")?null:Float.parseFloat(a[3])),
(a[4].equals("n")?null:a[4].equals("1")),
(a[5].equals("n")?null:a[5].equals("1")),
(a[6].equals("n")?null:a[6].equals("1")),
(a[7].equals("n")?null:a[7].equals("1")),
Float.parseFloat(a[8]),Float.parseFloat(a[9]),
Float.parseFloat(a[10]),
(a[11].equals("n")?null:a[11].equals("1")),
Float.parseFloat(a[12]),
Float.parseFloat(a[13]));
}
public RecordInputEvent(Boolean sneaking,
Boolean jumping,
Float movementSideways,
Float movementForward,
Boolean pressingForward,
Boolean pressingBack,
Boolean pressingLeft,
Boolean pressingRight,
float head_yaw,
float body_yaw,
float head_pitch,
Boolean sprinting,
float yaw,
float speed) {
this.sneaking = sneaking; this.sneaking = sneaking;
this.jumping = jumping;
this.movementSideways = movementSideways;
this.movementForward = movementForward;
this.pressingForward = pressingForward;
this.pressingBack = pressingBack;
this.pressingLeft = pressingLeft;
this.pressingRight = pressingRight;
this.head_yaw = head_yaw;
this.body_yaw = body_yaw;
this.pitch = head_pitch;
this.sprinting = sprinting;
this.yaw = yaw;
this.speed = speed;
}
public void fillEmpty(RecordInputEvent e) {
if (sneaking == null) sneaking = e.sneaking;
if (jumping == null) jumping = e.jumping;
if (movementSideways == null) movementSideways = e.movementSideways;
if (movementForward == null) movementForward = e.movementForward;
if (pressingForward == null) pressingForward = e.pressingForward;
if (pressingBack == null) pressingBack = e.pressingBack;
if (pressingLeft == null) pressingLeft = e.pressingLeft;
if (pressingRight == null) pressingRight = e.pressingRight;
if (sprinting == null) sprinting = e.sprinting;
}
public boolean isEmpty() {
return sneaking == null &&
jumping == null &&
movementSideways == null &&
movementForward == null &&
pressingForward == null &&
pressingBack == null &&
pressingLeft == null &&
pressingRight == null &&
sprinting == null;
} }
public void callback() { public void callback() {
RepeatingMod.replay_sneaking = sneaking; input_replay = this;
}
public void inputCallback() {
if (sprinting != null && client.player.isSprinting() != sprinting)
client.player.setSprinting(sprinting);
if (client.player.getYaw() != yaw)
client.player.setYaw(yaw);
if (client.player.getHeadYaw() != head_yaw)
client.player.setHeadYaw(head_yaw);
if (client.player.getBodyYaw() != body_yaw)
client.player.setBodyYaw(body_yaw);
if (client.player.getPitch() != pitch)
client.player.setPitch(pitch);
if (client.player.getMovementSpeed() != speed)
client.player.setMovementSpeed(speed);
if (sneaking != null && client.player.input.sneaking != sneaking)
client.player.input.sneaking = sneaking;
if (jumping != null && client.player.input.jumping != jumping)
client.player.input.jumping = jumping;
if (movementSideways != null && client.player.input.movementSideways != movementSideways)
client.player.input.movementSideways = movementSideways;
if (movementForward != null && client.player.input.movementForward != movementForward)
client.player.input.movementForward = movementForward;
if (pressingForward != null && client.player.input.pressingForward != pressingForward)
client.player.input.pressingForward = pressingForward;
if (pressingBack != null && client.player.input.pressingBack != pressingBack)
client.player.input.pressingBack = pressingBack;
if (pressingLeft != null && client.player.input.pressingLeft != pressingLeft)
client.player.input.pressingLeft = pressingLeft;
if (pressingRight != null && client.player.input.pressingRight != pressingRight)
client.player.input.pressingRight = pressingRight;
} }
public String toText() { public String toText() {
return "s="+(sneaking?"1":"0"); return "p="+
((sneaking==null)?"n":(sneaking?"1":"0"))+"&"+
((jumping==null)?"n":(jumping?"1":"0"))+"&"+
((movementSideways==null)?"n":movementSideways)+"&"+
((movementForward==null)?"n":movementForward)+"&"+
((pressingForward==null)?"n":(pressingForward?"1":"0"))+"&"+
((pressingBack==null)?"n":(pressingBack?"1":"0"))+"&"+
((pressingLeft==null)?"n":(pressingLeft?"1":"0"))+"&"+
((pressingRight==null)?"n":(pressingRight?"1":"0"))+"&"+
head_yaw+"&"+body_yaw+"&"+ pitch +"&"+
((sprinting==null)?"n":(sprinting?"1":"0")+
"&"+yaw+"&"+speed);
} }
public String getType() { public String getType() {
return "sneak"; return "input";
} }
} }
public static class RecordBlockBreakEvent extends RecordEvent { public static class RecordBlockBreakEvent extends RecordEvent {
public BlockPos pos; public BlockPos pos;
public static RecordBlockBreakEvent fromArgs(String[] a) {
return new RecordBlockBreakEvent(new BlockPos(
Integer.parseInt(a[0]),
Integer.parseInt(a[1]),
Integer.parseInt(a[2])));
}
public RecordBlockBreakEvent( public RecordBlockBreakEvent(
BlockPos pos) { BlockPos pos) {
this.pos = pos; this.pos = pos;
@ -318,6 +629,21 @@ public class RepeatingMod implements ClientModInitializer {
public Hand hand; public Hand hand;
public BlockHitResult hitResult; public BlockHitResult hitResult;
public static RecordBlockInteractEvent fromArgs(String[] a) {
return new RecordBlockInteractEvent(
Hand.valueOf(a[5]),
new BlockHitResult(new Vec3d(
Double.parseDouble(a[0]),
Double.parseDouble(a[1]),
Double.parseDouble(a[2])),
Direction.byId(Integer.parseInt(a[4])),
new BlockPos(
Integer.parseInt(a[0]),
Integer.parseInt(a[1]),
Integer.parseInt(a[2])),
a[3].equals("1")));
}
public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) { public RecordBlockInteractEvent(Hand hand, BlockHitResult hitResult) {
this.hand = hand; this.hand = hand;
this.hitResult = hitResult; this.hitResult = hitResult;

View File

@ -0,0 +1,371 @@
package themixray.repeating.mod;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.OptionSliderWidget;
import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
import java.awt.*;
import java.io.File;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Environment(EnvType.CLIENT)
public class RepeatingScreen extends Screen {
protected RepeatingScreen() {
super(Text.literal(""));
this.mod = RepeatingMod.me;
}
public RepeatingMod mod;
public ButtonWidget record_btn;
public ButtonWidget replay_btn;
public ButtonWidget loop_btn;
public ButtonWidget export_btn;
public ButtonWidget import_btn;
public SliderWidget pos_delay_slider;
public boolean was_build = false;
public void update_btns() {
if (was_build) {
replay_btn.setMessage(Text.translatable("text.repeating-mod." +
((mod.is_replaying) ? "stop_replay" : "start_replay")));
record_btn.setMessage(Text.translatable("text.repeating-mod." +
((mod.is_recording) ? "stop_record" : "start_record")));
loop_btn.setMessage(Text.of(((mod.loop_replay) ? "\uefff " : "\ueffe ")));
}
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
renderBackgroundTexture(context);
// context.drawCenteredTextWithShadow(textRenderer,
// Text.literal("You must see me"),
// width / 2, height / 2,
// Color.white.getRGB());
super.render(context, mouseX, mouseY, delta);
}
@Override
protected void init() {
record_btn = ButtonWidget.builder(
Text.translatable("text.repeating-mod.start_record"), button -> {
if (!mod.is_replaying) {
if (mod.is_recording)
mod.stopRecording();
else mod.startRecording();
update_btns();
}
})
.dimensions(width / 2 - 60, height / 2 - 54, 120, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.record_tooltip")))
.build();
replay_btn = ButtonWidget.builder(
Text.translatable("text.repeating-mod.start_replay"), button -> {
if (!mod.is_recording) {
if (mod.is_replaying)
mod.stopReplay();
else mod.startReplay();
update_btns();
}
})
.dimensions(width / 2 - 60, height / 2 - 32, 98, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.replay_tooltip")))
.build();
loop_btn = ButtonWidget.builder(Text.of(""), button -> {
mod.loop_replay = !mod.loop_replay;
update_btns();
})
.dimensions(width / 2 + 40, height / 2 - 32, 20, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.loop_tooltip")))
.build();
export_btn = ButtonWidget.builder(
Text.translatable("text.repeating-mod.export"), button -> {
if (mod.finish_record_pos == null) return;
StringBuilder t = new StringBuilder();
for (int i = 0; i < mod.record.size(); i++) {
t.append(mod.record.get(i).toText());
t.append("\n");
}
t.append(mod.start_record_pos.getX()+"n"+
mod.start_record_pos.getY()+"n"+
mod.start_record_pos.getZ()+"x"+
mod.finish_record_pos.getX()+"n"+
mod.finish_record_pos.getY()+"n"+
mod.finish_record_pos.getZ());
File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
if (!p.exists()) p.mkdir();
File file = new File(p,"export_"+
new SimpleDateFormat("MM_dd_yyyy").format(new Date())
+"_"+RepeatingMod.rand.nextInt(10)+".txt");
try {
if (!file.exists()) file.createNewFile();
Files.write(file.toPath(), t.toString().getBytes());
Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
} catch (Exception e) {
e.printStackTrace();
}
})
.dimensions(width / 2 - 60, height / 2 - 10, 120, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.export_tooltip")))
.build();
import_btn = ButtonWidget.builder(
Text.translatable("text.repeating-mod.import"), button -> {
mod.record.clear();
File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
if (!p.exists()) p.mkdir();
File file = new File(p,"import.txt");
try {
if (!file.exists()) {
file.createNewFile();
Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
return;
}
String t = Files.readString(file.toPath());
List<String> ss = List.of(t.split("\n"));
String ls = ss.get(ss.size()-1);
ss = ss.subList(0,ss.size()-1);
for (String s:ss)
mod.record.add(RepeatingMod.RecordEvent.fromText(s));
String[] lss0 = ls.split("x");
String[] lss1 = lss0[0].split("n");
String[] lss2 = lss0[1].split("n");
mod.start_record_pos = new Vec3d(
Float.parseFloat(lss1[0]),
Float.parseFloat(lss1[1]),
Float.parseFloat(lss1[2]));
mod.finish_record_pos = new Vec3d(
Float.parseFloat(lss2[0]),
Float.parseFloat(lss2[1]),
Float.parseFloat(lss2[2]));
} catch (Exception e) {
e.printStackTrace();
}
})
.dimensions(width / 2 - 60, height / 2 + 12, 120, 20)
.tooltip(Tooltip.of(Text.translatable("text.repeating-mod.import_tooltip")))
.build();
pos_delay_slider = new SliderWidget(
width / 2 - 60, height / 2 + 34, 120, 20,
(mod.record_pos_delay < 0) ? Text.translatable("text.repeating-mod.nan_pos_delay") :
Text.translatable("text.repeating-mod.pos_delay", String.valueOf(mod.record_pos_delay)),
(mod.record_pos_delay/10d+1d)/101d) {
@Override
protected void updateMessage() {
double v = value*101d-1d;
if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay"));
else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10))));
}
@Override
protected void applyValue() {
double v = value*101d-1d;
if (v <= 1) setMessage(Text.translatable("text.repeating-mod.nan_pos_delay"));
else setMessage(Text.translatable("text.repeating-mod.pos_delay", String.valueOf((long) (v*10))));
mod.record_pos_delay = (long) (v*10);
mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay));
mod.conf.save();
}
@Override
public void onRelease(double mouseX, double mouseY) {
super.onRelease(mouseX, mouseY);
applyValue();
}
@Override
protected void onDrag(double mouseX, double mouseY, double deltaX, double deltaY) {
super.onDrag(mouseX, mouseY, deltaX, deltaY);
applyValue();
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
updateMessage();
}
};
pos_delay_slider.setTooltip(Tooltip.of(Text.translatable("text.repeating-mod.pos_delay_tooltip")));
was_build = true;
update_btns();
addDrawableChild(replay_btn);
addDrawableChild(loop_btn);
addDrawableChild(record_btn);
addDrawableChild(export_btn);
addDrawableChild(import_btn);
addDrawableChild(pos_delay_slider);
// rootComponent
// .surface(Surface.VANILLA_TRANSLUCENT)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .verticalAlignment(VerticalAlignment.CENTER);
//
// replay_btn = (ButtonComponent) Components.button(Text.of("replay"),
// (ButtonComponent btn) -> {
// if (!mod.is_recording) {
// if (mod.is_replaying)
// mod.stopReplay();
// else mod.startReplay();
// update_btns();
// }
// }).margins(Insets.of(1)).sizing(
// Sizing.fixed(98),Sizing.fixed(20));
//
// loop_btn = (ButtonComponent) Components.button(Text.of(""),
// (ButtonComponent btn) -> {
// mod.loop_replay = !mod.loop_replay;
// update_btns();
// }).margins(Insets.of(1))
// .sizing(Sizing.fixed(20),Sizing.fixed(20));
//
// record_btn = (ButtonComponent) Components.button(Text.of("record"),
// (ButtonComponent btn) -> {
// if (!mod.is_replaying) {
// if (mod.is_recording)
// mod.stopRecording();
// else mod.startRecording();
// update_btns();
// }
// }).margins(Insets.of(1)).sizing(
// Sizing.fixed(120),Sizing.fixed(20));
// was_build = true;
//
// rootComponent.child(
// Containers.horizontalFlow(Sizing.content(), Sizing.content()).child(
// Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Components.label(Text.translatable("text.repeating-mod.basic")).margins(Insets.of(1)))
// .padding(Insets.of(5))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Containers.horizontalFlow(Sizing.content(), Sizing.content())
// .child(replay_btn).child(loop_btn))
// .child(record_btn)
// .child(Components.button(Text.translatable(
// "text.repeating-mod.export"),
// (ButtonComponent btn) -> {
// String t = "";
// for (int i = 0; i < mod.record.size(); i++) {
// t += mod.record.get(i).toText();
// if (i != mod.record.size()-1)
// t += "\n";
// }
//
// File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
// if (!p.exists()) p.mkdir();
// File file = new File(p,"export.txt");
//
// try {
// if (!file.exists()) file.createNewFile();
// Files.write(file.toPath(), t.getBytes());
// Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }).margins(Insets.of(10,1,1,1)).sizing(
// Sizing.fixed(120),Sizing.fixed(20)))
// .child(Components.button(Text.translatable(
// "text.repeating-mod.import"),
// (ButtonComponent btn) -> {
// mod.record.clear();
//
// File p = new File(FabricLoader.getInstance().getGameDir().toFile(),"repeating");
// if (!p.exists()) p.mkdir();
// File file = new File(p,"import.txt");
//
// try {
// if (!file.exists()) {
// file.createNewFile();
// Runtime.getRuntime().exec("explorer /select,\""+file.getAbsolutePath()+"\"");
// return;
// }
// String t = Files.readString(file.toPath());
// for (String s:t.split("\n"))
// mod.record.add(RepeatingMod.RecordEvent.fromText(s));
// } catch (Exception e) {
// e.printStackTrace();
// }
// }).margins(Insets.of(1)).sizing(
// Sizing.fixed(120),Sizing.fixed(20)))
// .padding(Insets.of(10))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))
// /*).child(
// Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Components.label(Text.translatable("text.repeating-mod.parkour")).margins(Insets.of(1)))
// .padding(Insets.of(5))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Components.label(Text.translatable("text.repeating-mod.dev")).margins(Insets.of(1)))
// .padding(Insets.of(10))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))*/
// ).child(
// Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Components.label(Text.translatable("text.repeating-mod.settings")).margins(Insets.of(1)))
// .padding(Insets.of(5))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))
// .child(Containers.verticalFlow(Sizing.content(), Sizing.content())
// .child(Components.discreteSlider(Sizing.fixed(120),-20,100)
// .setFromDiscreteValue(mod.record_pos_delay)
// .message((String s)->{
// mod.record_pos_delay = Long.parseLong(s);
// mod.conf.data.put("record_pos_delay",String.valueOf(mod.record_pos_delay));
// mod.conf.save();
// if (mod.record_pos_delay > -1)
// return Text.translatable("text.repeating-mod.pos_delay", s);
// return Text.translatable("text.repeating-mod.nan_pos_delay");
// }).scrollStep(25)
// .margins(Insets.of(1))
// .tooltip(Text.translatable("text.repeating-mod.pos_delay_text")))
// .padding(Insets.of(10))
// .surface(Surface.DARK_PANEL)
// .verticalAlignment(VerticalAlignment.CENTER)
// .horizontalAlignment(HorizontalAlignment.CENTER)
// .margins(Insets.of(1)))
// ));
update_btns();
}
}

View File

@ -0,0 +1,94 @@
package themixray.repeating.mod;
import java.util.ArrayList;
import java.util.List;
public abstract class TickTask implements Runnable {
public static List<TickTask> tasks = new ArrayList<>();
public static void tickTasks(TickAt at) {
for (TickTask t:new ArrayList<>(tasks))
if (t.getAt() == at) t.tick();
}
private long living;
private long delay;
private boolean is_repeating;
private long period;
private boolean is_cancelled;
private TickAt at;
public enum TickAt {
CLIENT_HEAD, CLIENT_TAIL,
MOVEMENT_HEAD, MOVEMENT_TAIL,
RENDER_HEAD, RENDER_TAIL
}
public TickTask(long delay, TickAt at) {
this.is_cancelled = false;
this.is_repeating = false;
this.delay = delay;
this.living = 0;
this.period = 0;
this.at = at;
tasks.add(this);
}
public TickTask(long delay, long period, TickAt at) {
this.is_cancelled = false;
this.is_repeating = true;
this.delay = delay;
this.period = period;
this.living = 0;
this.at = at;
tasks.add(this);
}
public TickTask(long delay) {
this(delay,TickAt.CLIENT_HEAD);
}
public TickTask(long delay, long period) {
this(delay,period,TickAt.CLIENT_HEAD);
}
public void cancel() {
if (!is_cancelled) {
is_cancelled = true;
tasks.remove(this);
}
}
public boolean isCancelled() {
return is_cancelled;
}
public TickAt getAt() {
return at;
}
public void setDelay(long delay) {
if (is_repeating) {
this.delay = delay;
}
}
public long getDelay() {
return this.delay;
}
public void tick() {
if (living >= delay) {
if (is_repeating) {
delay = period;
run();
living = -1;
} else {
run();
cancel();
}
}
living++;
}
}

View File

@ -0,0 +1,24 @@
package themixray.repeating.mod.mixin;
import net.minecraft.client.MinecraftClient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask;
@Mixin(MinecraftClient.class)
public abstract class ClientMixin {
@Inject(at = @At(value = "HEAD"), method = "tick")
private void onTickHead(CallbackInfo ci) {
if (RepeatingMod.me.is_recording)
RepeatingMod.me.recordAllInput();
TickTask.tickTasks(TickTask.TickAt.CLIENT_HEAD);
}
@Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.CLIENT_TAIL);
}
}

View File

@ -0,0 +1,31 @@
package themixray.repeating.mod.mixin;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask;
import java.util.UUID;
@Mixin(Entity.class)
public abstract class EntityMixin {
@Shadow public abstract UUID getUuid();
@Inject(at = @At(value = "HEAD"), method = "setSprinting", cancellable = true)
private void onSprint(boolean sprinting,CallbackInfo ci) {
if (getUuid().equals(RepeatingMod.client.player.getUuid())) {
if (RepeatingMod.me.is_replaying) {
if (RepeatingMod.input_replay != null &&
RepeatingMod.input_replay.sprinting != null &&
RepeatingMod.input_replay.sprinting != sprinting) {
ci.cancel();
return;
}
}
}
}
}

View File

@ -0,0 +1,20 @@
package themixray.repeating.mod.mixin;
import net.minecraft.client.input.KeyboardInput;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
@Mixin(KeyboardInput.class)
public abstract class InputMixin {
@Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(boolean slowDown, float f, CallbackInfo ci) {
if (RepeatingMod.me.is_replaying) {
if (RepeatingMod.input_replay != null) {
RepeatingMod.input_replay.inputCallback();
}
}
}
}

View File

@ -2,31 +2,20 @@ package themixray.repeating.mod.mixin;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.MovementType;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod; import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask;
import java.util.Date; import java.util.ArrayList;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
public abstract class MovementMixin { public abstract class MovementMixin {
public Vec3d lastVec = null;
@Shadow public abstract void sendMessage(Text message);
@Shadow @Final protected MinecraftClient client;
@Shadow public abstract float getYaw(float tickDelta);
@Shadow private float lastYaw;
@Shadow private float lastPitch;
@Inject(at = @At(value = "HEAD"), method = "init") @Inject(at = @At(value = "HEAD"), method = "init")
private void init(CallbackInfo ci) { private void init(CallbackInfo ci) {
@ -43,50 +32,13 @@ public abstract class MovementMixin {
}); });
} }
@Inject(at = @At(value = "HEAD"), method = "move") @Inject(at = @At(value = "HEAD"), method = "tickMovement")
private void onMove(MovementType movementType, Vec3d vec, CallbackInfo ci) { private void onMoveHead(CallbackInfo ci) {
if (RepeatingMod.me.is_recording) { TickTask.tickTasks(TickTask.TickAt.MOVEMENT_HEAD);
if (vec != lastVec) { }
double dist = 0;
if (lastVec != null)
dist = vec.distanceTo(lastVec);
if (dist > 0.0) {
Vec3d c = client.player.getPos();
RepeatingMod.RecordMoveEvent ev = new RepeatingMod.RecordMoveEvent( @Inject(at = @At(value = "TAIL"), method = "tick")
new Vec3d(c.getX() + vec.getX(), private void onMoveTail(CallbackInfo ci) {
c.getY() + vec.getY(), TickTask.tickTasks(TickTask.TickAt.MOVEMENT_TAIL);
c.getZ() + vec.getZ()),
lastYaw, lastPitch);
boolean just_add = true;
Date now = new Date();
if (RepeatingMod.me.last_record != null) {
long diff = now.getTime() - RepeatingMod.me.last_record.getTime();
boolean add_delay = true;
if (diff > 0) {
RepeatingMod.RecordEvent last_ev = RepeatingMod.me.record.get(RepeatingMod.me.record.size()-1);
if (last_ev instanceof RepeatingMod.RecordMoveEvent) {
RepeatingMod.RecordMoveEvent last_ev1 = (RepeatingMod.RecordMoveEvent) last_ev;
if (last_ev1.vec.distanceTo(ev.vec) < RepeatingMod.me.record_blocks_limit &&
diff < RepeatingMod.me.record_time_limit) {
just_add = false;
add_delay = false;
last_ev1.vec = ev.vec;
}
}
}
if (add_delay) {
RepeatingMod.me.record.add(new RepeatingMod.RecordDelayEvent(diff));
}
}
if (just_add) {
RepeatingMod.me.record.add(ev);
RepeatingMod.me.last_record = now;
}
}
}
lastVec = vec;
}
} }
} }

View File

@ -0,0 +1,23 @@
package themixray.repeating.mod.mixin;
import net.minecraft.client.input.KeyboardInput;
import net.minecraft.client.render.*;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import themixray.repeating.mod.RepeatingMod;
import themixray.repeating.mod.TickTask;
@Mixin(GameRenderer.class)
public abstract class RendererMixin {
@Inject(at = @At(value = "HEAD"), method = "tick")
private void onTickHead(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.RENDER_HEAD);
}
@Inject(at = @At(value = "TAIL"), method = "tick")
private void onTickTail(CallbackInfo ci) {
TickTask.tickTasks(TickTask.TickAt.RENDER_TAIL);
}
}

View File

@ -0,0 +1,200 @@
package themixray.repeating.mod.render;
import lombok.experimental.UtilityClass;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.util.math.Vec3d;
import themixray.repeating.mod.render.buffer.WorldBuffer;
import themixray.repeating.mod.render.shader.ShaderManager;
import java.awt.*;
import static org.lwjgl.opengl.GL33.*;
@UtilityClass
public class RenderHelper {
public WorldBuffer startLines(WorldRenderContext context) {
glEnable(GL_LINE_SMOOTH);
return new WorldBuffer(GL_LINES, ShaderManager.getPositionColorShader(), context);
}
public void endLines(WorldBuffer buffer) {
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
buffer.draw();
glDepthMask(true);
glDisable(GL_BLEND);
}
public void drawLine(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, Color color) {
buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
}
public WorldBuffer startTri(WorldRenderContext context) {
return new WorldBuffer(GL_TRIANGLES, ShaderManager.getPositionColorShader(), context);
}
public void endTri(WorldBuffer buffer) {
//glDepthRange(0, 0.7);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDepthMask(false);
buffer.draw();
glDepthMask(true);
glEnable(GL_CULL_FACE);
glDisable(GL_BLEND);
glDepthRange(0, 1);
}
public void drawTri(WorldBuffer buffer, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, Color color) {
buffer.vert(x1, y1, z1, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x2, y2, z2, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
buffer.vert(x3, y3, z3, color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, color.getAlpha() / 255f);
}
public void drawRectFromTri(WorldBuffer buffer,
float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4,
Color color) {
drawTri(buffer,
x1, y1, z1,
x2, y2, z2,
x3, y3, z3,
color);
drawTri(buffer,
x3, y3, z3,
x4, y4, z4,
x1, y1, z1,
color);
}
public void drawRectFromLines(WorldBuffer buffer,
float x1, float y1, float z1,
float x2, float y2, float z2,
float x3, float y3, float z3,
float x4, float y4, float z4,
Color color) {
drawLine(buffer,
x1, y1, z1,
x2, y2, z2,
color);
drawLine(buffer,
x2, y2, z2,
x3, y3, z3,
color);
drawLine(buffer,
x3, y3, z3,
x4, y4, z4,
color);
drawLine(buffer,
x4, y4, z4,
x1, y1, z1,
color);
}
public void drawBoxFromTri(WorldBuffer buffer,
float x1, float y1, float z1,
float x2, float y2, float z2,
Color color) {
float[][] v = new float[][]{
new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)},
new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}};
drawRectFromTri(buffer,
v[0][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[0][2],
v[1][0], v[1][1], v[0][2],
v[0][0], v[1][1], v[0][2],
color);
drawRectFromTri(buffer,
v[0][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[1][2],
v[0][0], v[0][1], v[1][2],
color);
drawRectFromTri(buffer,
v[0][0], v[0][1], v[0][2],
v[0][0], v[0][1], v[1][2],
v[0][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[0][2],
color);
drawRectFromTri(buffer,
v[0][0], v[0][1], v[1][2],
v[1][0], v[0][1], v[1][2],
v[1][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[1][2],
color);
drawRectFromTri(buffer,
v[0][0], v[1][1], v[0][2],
v[1][0], v[1][1], v[0][2],
v[1][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[1][2],
color);
drawRectFromTri(buffer,
v[1][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[1][2],
v[1][0], v[1][1], v[1][2],
v[1][0], v[1][1], v[0][2],
color);
}
public void drawBoxFromLines(WorldBuffer buffer,
float x1, float y1, float z1,
float x2, float y2, float z2,
Color color) {
float[][] v = new float[][]{
new float[]{Math.min(x1, x2), Math.min(y1, y2), Math.min(z1, z2)},
new float[]{Math.max(x1, x2), Math.max(y1, y2), Math.max(z1, z2)}};
drawRectFromLines(buffer,
v[0][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[0][2],
v[1][0], v[1][1], v[0][2],
v[0][0], v[1][1], v[0][2],
color);
drawRectFromLines(buffer,
v[0][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[1][2],
v[0][0], v[0][1], v[1][2],
color);
drawRectFromLines(buffer,
v[0][0], v[0][1], v[0][2],
v[0][0], v[0][1], v[1][2],
v[0][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[0][2],
color);
drawRectFromLines(buffer,
v[0][0], v[0][1], v[1][2],
v[1][0], v[0][1], v[1][2],
v[1][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[1][2],
color);
drawRectFromLines(buffer,
v[0][0], v[1][1], v[0][2],
v[1][0], v[1][1], v[0][2],
v[1][0], v[1][1], v[1][2],
v[0][0], v[1][1], v[1][2],
color);
drawRectFromLines(buffer,
v[1][0], v[0][1], v[0][2],
v[1][0], v[0][1], v[1][2],
v[1][0], v[1][1], v[1][2],
v[1][0], v[1][1], v[0][2],
color);
}
}

View File

@ -0,0 +1,13 @@
package themixray.repeating.mod.render;
import lombok.experimental.UtilityClass;
import themixray.repeating.mod.render.buffer.BufferManager;
import themixray.repeating.mod.render.shader.ShaderManager;
@UtilityClass
public class RenderSystem {
public void init() {
BufferManager.init();
ShaderManager.init();
}
}

View File

@ -0,0 +1,48 @@
package themixray.repeating.mod.render.buffer;
import lombok.experimental.UtilityClass;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL33.*;
@UtilityClass
public class BufferManager {
private int vao;
private int vbo;
private int prevVao;
public void init() {
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
vao = glGenVertexArrays();
vbo = glGenBuffers();
});
}
public void bindBuffer() {
glBindBuffer(GL_ARRAY_BUFFER, vbo);
}
public void unbindBuffer() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void writeBuffer(FloatBuffer buffer) {
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
}
public void draw(int drawMode, int verts) {
glDrawArrays(drawMode, 0, verts);
}
public void bind() {
prevVao = glGetInteger(GL_VERTEX_ARRAY_BINDING);
glBindVertexArray(vao);
}
public void unbind() {
glBindVertexArray(prevVao);
}
}

View File

@ -0,0 +1,30 @@
package themixray.repeating.mod.render.buffer;
import lombok.Getter;
public class Vertex {
@Getter
private float x;
@Getter
private float y;
@Getter
private float z;
@Getter
private float r;
@Getter
private float g;
@Getter
private float b;
@Getter
private float a;
public Vertex(float x, float y, float z, float r, float g, float b, float a) {
this.x = x;
this.y = y;
this.z = z;
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
}

View File

@ -0,0 +1,82 @@
package themixray.repeating.mod.render.buffer;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.util.math.Vec3d;
import org.apache.commons.lang3.ArrayUtils;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import themixray.repeating.mod.render.shader.Shader;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.opengl.GL33.*;
public class WorldBuffer {
private final List<Vertex> vertices = new ArrayList<>();
private final int drawMode;
private final Shader shader;
private FloatBuffer projectionMatrix;
private final Vec3d cameraPos;
public WorldBuffer(int drawMode, Shader shader, WorldRenderContext worldRenderContext) {
this.drawMode = drawMode;
this.shader = shader;
this.cameraPos = worldRenderContext.camera().getPos();
makeProjectionMatrix(worldRenderContext.projectionMatrix(), worldRenderContext.matrixStack().peek().getPositionMatrix());
}
public void vert(float x, float y, float z, float r, float g, float b, float a) {
vertices.add(new Vertex(x - (float) cameraPos.x, y - (float) cameraPos.y, z - (float) cameraPos.z, r, g, b, a));
}
public void draw() {
BufferManager.bind();
BufferManager.bindBuffer();
BufferManager.writeBuffer(getBuffer());
applyProjectionMatrix();
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, vertices.size() * 3 * 4L);
glEnableVertexAttribArray(1);
BufferManager.unbindBuffer();
shader.bind();
BufferManager.draw(drawMode, this.vertices.size());
shader.unbind();
BufferManager.unbind();
}
private FloatBuffer getBuffer() {
FloatBuffer floatBuffer = BufferUtils.createFloatBuffer(vertices.size() * 7);
ArrayList<Float> floats = new ArrayList<>();
for (Vertex vertex : vertices) {
floats.add(vertex.getX());
floats.add(vertex.getY());
floats.add(vertex.getZ());
}
for (Vertex vertex : vertices) {
floats.add(vertex.getR());
floats.add(vertex.getG());
floats.add(vertex.getB());
floats.add(vertex.getA());
}
Float[] floatArray = new Float[floats.size()];
floats.toArray(floatArray);
floatBuffer.put(ArrayUtils.toPrimitive(floatArray));
return floatBuffer.flip();
}
private void makeProjectionMatrix(Matrix4f projectionMatrix, Matrix4f viewModelMatrix) {
this.projectionMatrix = projectionMatrix.mul(viewModelMatrix).get(BufferUtils.createFloatBuffer(16));
}
private void applyProjectionMatrix() {
shader.uniformMatrix4f("u_projection", projectionMatrix);
}
}

View File

@ -0,0 +1,42 @@
package themixray.repeating.mod.render.shader;
import lombok.Getter;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL33.*;
public class Shader {
@Getter
private final int id;
public Shader(String name) {
int v = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.VERTEX);
int f = ShaderManager.loadShaderProgram(name, ShaderManager.ShaderType.FRAGMENT);
this.id = glCreateProgram();
glAttachShader(id, v);
glAttachShader(id, f);
glLinkProgram(id);
}
public void bind() {
glUseProgram(id);
}
public void unbind() {
glUseProgram(0);
}
public void uniformMatrix4f(String name, FloatBuffer matrix) {
bind();
glUniformMatrix4fv(glGetUniformLocation(id, name), false, matrix);
unbind();
}
public void uniformValue2f(String name, float value1, float value2) {
bind();
glUniform2f(glGetUniformLocation(id, name), value1, value2);
unbind();
}
}

View File

@ -0,0 +1,97 @@
package themixray.repeating.mod.render.shader;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.TextureUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.GlImportProcessor;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceFactory;
import net.minecraft.util.Identifier;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.system.MemoryUtil;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import static org.lwjgl.opengl.GL33.*;
@UtilityClass
public class ShaderManager {
@Getter
private Shader positionColorShader;
public void init() {
ClientLifecycleEvents.CLIENT_STARTED.register(client -> loadShaders());
}
private void loadShaders() {
positionColorShader = new Shader("position_color");
}
public int loadShaderProgram(String name, ShaderType type) {
try {
boolean file_present = true;
ResourceFactory resourceFactory = MinecraftClient.getInstance().getResourceManager();
Optional<Resource> resource = resourceFactory.getResource(new Identifier("renderer", "shader/" + name + type.fileExtension));
int i = glCreateShader(type.glType);
if (resource.isPresent()) {
GlStateManager.glShaderSource(i, new GlImportProcessor() {
@SneakyThrows
@Nullable
@Override
public String loadImport(boolean inline, String name) {
return IOUtils.toString(resource.get().getInputStream(), StandardCharsets.UTF_8);
}
}.readSource(readResourceAsString(resource.get().getInputStream())));
} else file_present = false;
glCompileShader(i);
if (glGetShaderi(i, GL_COMPILE_STATUS) == 0 || !file_present) {
String shaderInfo = StringUtils.trim(glGetShaderInfoLog(i, 32768));
throw new IOException("Couldn't compile " + type.name + " program (" + name + ") : " + shaderInfo);
}
return i;
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
private String readResourceAsString(InputStream inputStream) {
ByteBuffer byteBuffer = null;
try {
byteBuffer = TextureUtil.readResource(inputStream);
int i = byteBuffer.position();
byteBuffer.rewind();
return MemoryUtil.memASCII(byteBuffer, i);
} catch (IOException ignored) {
} finally {
if (byteBuffer != null) {
MemoryUtil.memFree(byteBuffer);
}
}
return null;
}
public enum ShaderType {
VERTEX("vertex", ".vsh", GL_VERTEX_SHADER),
FRAGMENT("fragment", ".fsh", GL_FRAGMENT_SHADER);
private final String name;
private final String fileExtension;
private final int glType;
ShaderType(String name, String fileExtension, int glType) {
this.name = name;
this.fileExtension = fileExtension;
this.glType = glType;
}
}
}

View File

@ -0,0 +1,17 @@
{
"providers": [
{
"file":"repeating-mod:ui/no-loop.png",
"chars":["\ueffe"],
"height":16,
"ascent":12,
"type":"bitmap"
},{
"file":"repeating-mod:ui/loop.png",
"chars":["\uefff"],
"height":16,
"ascent":12,
"type":"bitmap"
}
]
}

View File

@ -0,0 +1,12 @@
#version 330
in vec4 vertexColor;
out vec4 fragmentColor;
void main() {
if (vertexColor.a == 0.0f) {
discard;
}
fragmentColor = vertexColor;
}

View File

@ -0,0 +1,13 @@
#version 330
layout (location = 0) in vec3 i_pos;
layout (location = 1) in vec4 i_color;
uniform mat4 u_projection;
out vec4 vertexColor;
void main() {
gl_Position = u_projection * vec4(i_pos, 1.0f);
vertexColor = i_color;
}

View File

@ -4,20 +4,21 @@
"key.repeating-mod.toggle_record": "Toggle recording", "key.repeating-mod.toggle_record": "Toggle recording",
"text.repeating-mod.name": "Repeating Mod", "text.repeating-mod.name": "Repeating Mod",
"text.repeating-mod.record": "record", "text.repeating-mod.start_record": "Start record",
"text.repeating-mod.replay": "replay", "text.repeating-mod.stop_record": "Stop record",
"text.repeating-mod.start": "Start", "text.repeating-mod.start_replay": "Start replay",
"text.repeating-mod.stop": "Stop", "text.repeating-mod.stop_replay": "Stop replay",
"text.repeating-mod.record_tooltip": "Start/stop recording all activities",
"text.repeating-mod.replay_tooltip": "Start/stop repeating recorded actions",
"text.repeating-mod.loop_tooltip": "Enable/disable repeating of recorded actions replay",
"text.repeating-mod.export": "Export record", "text.repeating-mod.export": "Export record",
"text.repeating-mod.import": "Import record", "text.repeating-mod.import": "Import record",
"text.repeating-mod.basic": "Basic mode", "text.repeating-mod.export_tooltip": "Exporting a recording to a file",
"text.repeating-mod.parkour": "Parkour mode", "text.repeating-mod.import_tooltip": "Importing an entry from the import.txt file",
"text.repeating-mod.settings": "Settings",
"text.repeating-mod.dev": "In development...", "text.repeating-mod.dev": "In development...",
"text.repeating-mod.block_limit": "Block limit: %s", "text.repeating-mod.nan_pos_delay": "No pos timer",
"text.repeating-mod.block_limit_tooltip": "Two recording events will\nbe summed up if the\ndistance between them is\nless than the limit.", "text.repeating-mod.pos_delay": "Pos timer: %s ticks",
"text.repeating-mod.time_limit": "Time limit: %s ms", "text.repeating-mod.pos_delay_tooltip": "Timer after which the pos\nevent is added (20 ticks = 1 sec)",
"text.repeating-mod.time_limit_tooltip": "Two recording events will\nbe summed up if the time\nbetween them is less than\nthe limit.",
"message.repeating-mod.replay_start": "Replay started", "message.repeating-mod.replay_start": "Replay started",
"message.repeating-mod.replay_stop": "Replay finished", "message.repeating-mod.replay_stop": "Replay finished",

View File

@ -4,20 +4,21 @@
"key.repeating-mod.toggle_record": "Вкл/выкл запись", "key.repeating-mod.toggle_record": "Вкл/выкл запись",
"text.repeating-mod.name": "Репитинг Мод", "text.repeating-mod.name": "Репитинг Мод",
"text.repeating-mod.record": "запись", "text.repeating-mod.start_record": "Начать запись",
"text.repeating-mod.replay": "повтор", "text.repeating-mod.stop_record": "Остановить запись",
"text.repeating-mod.start": "Начать", "text.repeating-mod.start_replay": "Начать повтор",
"text.repeating-mod.stop": "Остановить", "text.repeating-mod.stop_replay": "Остановить повтор",
"text.repeating-mod.record_tooltip": "Начать/остановить запись всех действий",
"text.repeating-mod.replay_tooltip": "Начать/остановить повтор записанных действий",
"text.repeating-mod.loop_tooltip": "Вкл/выкл повтор повтора записанных действий",
"text.repeating-mod.export": "Экспорт записи", "text.repeating-mod.export": "Экспорт записи",
"text.repeating-mod.import": "Импорт записи", "text.repeating-mod.import": "Импорт записи",
"text.repeating-mod.basic": "Обычный режим", "text.repeating-mod.export_tooltip": "Экспорт записи в файл",
"text.repeating-mod.parkour": "Режим паркура", "text.repeating-mod.import_tooltip": "Импорт записи из файла import.txt",
"text.repeating-mod.settings": "Настройки",
"text.repeating-mod.dev": "В разработке...", "text.repeating-mod.dev": "В разработке...",
"text.repeating-mod.block_limit": "Лимит блоков: %s", "text.repeating-mod.nan_pos_delay": "Таймера позиции нету",
"text.repeating-mod.block_limit_tooltip": "Два ивента записи будут\nсуммироваться, если\nрасстояние между ними\nменьше лимита.", "text.repeating-mod.pos_delay": "Таймер позиции: %s тиков",
"text.repeating-mod.time_limit": "Лимит времени: %s мс", "text.repeating-mod.pos_delay_tooltip": "Таймер, после которой добавляется\nивент позиции (20 тиков = 1 сек)",
"text.repeating-mod.time_limit_tooltip": "Два ивента записи будут\nсуммироваться, если время\nмежду ними меньше лимита.",
"message.repeating-mod.replay_start": "Повтор начат", "message.repeating-mod.replay_start": "Повтор начат",
"message.repeating-mod.replay_stop": "Повтор закончен", "message.repeating-mod.replay_stop": "Повтор закончен",

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

View File

@ -4,13 +4,13 @@
"version": "${version}", "version": "${version}",
"name": "Repeating Mod", "name": "Repeating Mod",
"description": "Mod that repeats your actions. ", "description": "Mod that repeats your recorded actions. ",
"authors": [ "authors": [
"TheMixRay" "TheMixRay"
], ],
"contact": { "contact": {
"homepage": "https://fabricmc.net/", "homepage": "https://modrinth.com/mod/repeating-mod",
"sources": "https://github.com/FabricMC/fabric-example-mod" "sources": "https://github.com/MeexReay/repeating-mod"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
@ -29,7 +29,7 @@
"depends": { "depends": {
"fabricloader": ">=0.14.14", "fabricloader": ">=0.14.14",
"fabric-api": "*", "fabric-api": "*",
"minecraft": "~1.19.3", "minecraft": ">=1.20",
"java": ">=17" "java": ">=17"
}, },
"suggests": { "suggests": {

BIN
src/main/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -7,7 +7,10 @@
], ],
"client": [ "client": [
"MovementMixin", "MovementMixin",
"InputMixin" "InputMixin",
"RendererMixin",
"EntityMixin",
"ClientMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1