Protokoll
Leitungen
Drei Wege rein. Such dir eine Leitung aus.
Programme
Geladen und einsatzbereit.
Code
Zwischen den Streams baue ich Dinge, die laufen: Spielsysteme für Multiplayer-Server, Discord-Bots, Web-Apps. Drei echte Ausschnitte aus meinen Projekten.
Gangsystem für einen Rollenspiel-Server. Wer ein Gebiet hält, bekommt einmal am Tag die hinterlegten Buffs: Geld auf das Gangkonto, Items an alle Mitglieder online oder ein Serverbefehl. Jede Auszahlung landet im Log.
local function pay(organization, territory, buff)
if buff.type == 'bank' or buff.type == 'money' then
MySQL.update.await('UPDATE cc_crime_organizations SET balance=balance+? WHERE id=?',
{ tonumber(buff.amount) or 0, organization.id })
elseif buff.type == 'item' and QBCore.Shared.Items[buff.item] then
for _, id in ipairs(GetPlayers()) do
local member = CCCrime.GetOrganizationByPlayer(tonumber(id))
if member and member.id == organization.id then
local player = QBCore.Functions.GetPlayer(tonumber(id))
if player then player.Functions.AddItem(buff.item, tonumber(buff.amount) or 1) end
end
end
elseif buff.type == 'command' and buff.command then
ExecuteCommand(buff.command:gsub('{organization}', organization.id))
end
MySQL.insert.await('INSERT INTO cc_crime_reward_log (territory_id, organization_id, buff_json) VALUES (?,?,?)',
{ territory.id, organization.id, json.encode(buff) })
end
CreateThread(function()
while true do
Wait(60000)
local utc = os.date('!*t')
if utc.hour == Config.DailyRewardHourUtc and utc.min == 0 then
for _, territory in ipairs(ownedTerritories()) do
for _, buff in ipairs(json.decode(territory.buffs_json or '[]')) do
pay(territory.owner, territory, buff)
end
end
Wait(61000)
end
end
end)
Der Bot für meine Community. Jeder Befehl ist eine eigene Datei im Ordner commands. Beim Start werden alle eingesammelt, bei Discord registriert und die Module für Voice, Status und Verifizierung erst geladen, wenn der Client wirklich bereit ist.
client.commands = new Collection();
const commandsArray = [];
const commandsPath = path.join(__dirname, 'commands');
for (const file of fs.readdirSync(commandsPath).filter(f => f.endsWith('.js'))) {
const command = require(path.join(commandsPath, file));
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
commandsArray.push(command.data.toJSON());
}
}
client.once('clientReady', async () => {
console.log(`Eingeloggt als ${client.user.tag}!`);
await rest.put(Routes.applicationCommands(client.user.id), { body: commandsArray });
// Features erst laden, wenn der Client bereit ist
require('./features/voiceChannel')(client, pool);
require('./features/serverStatus')(client);
require('./features/voiceSupport')(client, pool);
require('./features/welcome')(client, pool);
});
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (err) {
console.error(err);
await interaction.reply({ content: 'Da ist etwas schiefgelaufen.', ephemeral: true });
}
});
R6CARDS, ein Sammelkartenspiel rund um Rainbow Six. Der Rückkanal des Discord-Logins tauscht den Code gegen eine Session und schickt den Nutzer dorthin zurück, wo er war. Nur seiteninterne Ziele sind erlaubt.
import { NextResponse, type NextRequest } from "next/server";
import { createClient } from "@/lib/supabase/server";
export async function GET(request: NextRequest) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
const next = searchParams.get("next");
// Nur seiteninterne Ziele zulassen, sonst wäre das eine offene Weiterleitung.
const target = next && next.startsWith("/") && !next.startsWith("//") ? next : "/collection";
if (!code) {
return NextResponse.redirect(`${origin}/?error=auth`);
}
const supabase = await createClient();
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (error) {
return NextResponse.redirect(`${origin}/?error=auth`);
}
return NextResponse.redirect(`${origin}${target}`);
}