This guide is for making .tuff mods in TuffClient. it's also for how to enable and manage the mods you installed.
A .tuff file is JSON
It is made of two things:
meta(mod info. ex. your username, minimum client version, etc.)files(your JS code, what you want the mod to do)
- Press
Right Shift - Press
Add Modsat the bottom - Pick one or more
.tufffiles - Press
Manage Modsto enable, disable, uninstall, etc. Basically just manage the mods.
If a mod makes big changes, it can use api.reloadClient(). I added this because well: for a Fabric mod you always have to restart your game, so if your mod does something that will need a reload, use that.
{
"meta": {
"id": "my-mod-id",
"name": "My Mod",
"version": "1.0.0",
"author": "You",
"entry": "main.js",
"minClientVersion": "1.0.0",
"permissions": ["hud", "events", "modules"]
},
"files": {
"main.js": "module.exports = function(api){ api.log('loaded'); };"
}
}meta.idmust start with a letter or number, and only use letters, numbers, or - and _meta.entrymust exist infiles(thats the js file name. ex."main.js": "module.exports = function(api){ api.log('loaded'); };"here in that example it's main.js)permissionsmust be an strings. They tell the user what the mod does. What it changes.minClientVersionmust not be newer than the client. If it is, it will fail to open.- Entry must export a function:
module.exports = function(api){ ... }(you need to use the runtime API, or else my JS api won't work)
You can make a lot with this system, not only UI:
- HUD widgets (text, timers, status)
- PvP trainers (like WTap timing help)
- QoL tools (chat macros, key tools, view tools)
- Camera/perspective tools (freelook, third person stuff)
- Cosmetic changes and client side changes
- Settings saved per mod
- Tick based stuff and key based stuff
tick->{ now, delta }render2d->{ ctx, width, height, partialTicks }keyDown->{ keyCode, isRepeat }keyUp->{ keyCode }attackEntity->{ targetId, isPlayer, targetName?, reach? }
Key codes can be found on the Minecraft Wiki
api.versionapi.on(event, fn)/api.off(event, fn)api.registerHUD(id, renderFn)/api.unregisterHUD(id)api.registerModule(def)api.getSetting(key, fallback)api.setSetting(key, value)api.drawText(ctx, text, x, y, opts?)api.toast(message, type?)api.log(...args)
These can change gameplay too:
api.setPerspective(mode)where mode is0,1,2api.getPerspective()api.setFreelookEnabled(bool)api.isFreelookEnabled()api.resetFreelook()api.sendChat(message)api.setSprint(bool)api.setSneak(bool)api.setHudHidden(bool)api.setGamma(number)api.setFov(number)api.reloadClient()
If you add "unsafe" in permissions, your mod gets full power mode.
"permissions": ["hud", "events", "modules", "unsafe"]What it does:
- allows browser globals in runtime
- allows
evalandFunction(these allow you to do more complex things) - allows
api.getUnsafeGlobal()
Use this careful, because unsafe mods can change almost everything.
Mods should stay legit:
- HUD
- trainers
- cosmetics
- QoL These are safe examples
Do not make cheat mods: aim assist, velocity, auto clickers or unfair reach.
Rendering mods are mods that change what the player views on their screen, without changing server.
- Make custom text and status on screen with
api.registerHUD(...) - Update visuals every frame with the
render2devent - React every tick with the
tickevent - Change camera/view settings:
api.setPerspective(mode)(0first person,1third person back,2third person front)api.setFreelookEnabled(bool)api.resetFreelook()
- Control entity culling for non-player entities:
api.setMobCullingEnabled(bool)api.setMobCullingDistance(number)
Players are rendered normal. Culling only affects non-player entities.
module.exports = function(api){
api.registerHUD("my-hud", function(ctx){
if(!ctx) return;
api.drawText(ctx, "My Render Mod Active", 10, 20, {
color: "#ffffff",
font: "16px monospace"
});
});
};
## 12. Example 1: Hello HUD
```json
{
"meta": {
"id": "hello-hud",
"name": "Hello HUD",
"version": "1.0.0",
"author": "Community",
"entry": "main.js",
"minClientVersion": "1.0.0",
"permissions": ["hud", "events", "modules"]
},
"files": {
"main.js": "module.exports = function(api){ api.registerHUD('hello', function(ctx){ if(!ctx) return; api.drawText(ctx, 'Tuff Mod Loaded', 10, 20, { color: '#ffffff', font: '16px monospace' }); }); };"
}
}{
"meta": {
"id": "freelook-toggle",
"name": "Freelook Toggle",
"version": "1.0.0",
"author": "Community",
"entry": "main.js",
"minClientVersion": "1.0.0",
"permissions": ["hud", "events", "modules"]
},
"files": {
"main.js": "module.exports = function(api){ var key=46; var on=false; api.on('keyDown', function(e){ if(e.keyCode===key && !e.isRepeat){ on=!on; api.setFreelookEnabled(on); if(!on){ api.resetFreelook(); } } }); api.registerHUD('free', function(ctx){ if(!ctx||!on) return; api.drawText(ctx, 'Freelook ON', 10, 40, { color:'#aaffaa', font:'16px monospace' }); }); };"
}
}{
"meta": {
"id": "quick-gg",
"name": "Quick GG",
"version": "1.0.0",
"author": "Community",
"entry": "main.js",
"minClientVersion": "1.0.0",
"permissions": ["hud", "events", "modules"]
},
"files": {
"main.js": "module.exports = function(api){ var key=34; api.on('keyDown', function(e){ if(e.keyCode===key && !e.isRepeat){ api.sendChat('gg'); api.toast('Sent: gg'); } }); };"
}
}- If install fails read the stacktrace in the text popup.
- Keep
main.jssmall at first and then if it works make it bigger - Check JSON commas, etc.
- Check
meta.entryfile name exactly matches. - If a mod acts weird, reload once and test again.