GC-Proto/op.js

1000 lines
25 KiB
JavaScript

// Removed useless files, to speed up build process and make it clearer.
const path = require("path");
const fs = require("fs");
const readline = require("readline");
// cmdid current version
const read_cmdid_last = "cmdid_last.csv";
const read_cmdid = "cmdid.csv";
const read_cmdid_output = "cmdid.json";
const read_cmdid_ht_output = "cmdid_ht_40.json";
const read_cmdid_output_gc = "cmdid_gc.json";
const read_cmdid_output_gc_update = "cmdid_gc_update.json";
const read_cmdid_output_gc_nofound = "cmdid_gc_nofound.json";
const file_gc_needed = "gc_needed.json";
const file_gc_needed2 = "gc_needed2.json";
const write_op = "PacketOpcodes.java";
console.log(process.cwd());
//(TODO: add input file)
// folder gc auto-generated proto
const folder_proto_gc_gen =
"../GSServer-GC/src/generated/main/java/emu/grasscutter/net/proto/";
// file PacketOpcodes currently in use
const read_cmdid_gc =
"../GSServer-GC/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java";
const folder_packet_gc =
"../GSServer-GC/src/main/java/emu/grasscutter/server/packet/";
const folder_gc_scan = "../GSServer-GCOriginal/src/main/java/emu/grasscutter/";
//const read_cmdid = fs.readFileSync("cmdid.csv");
//const read_packetopcodes = fs.readFileSync("PacketOpcodes.java");
var data = [];
var data_gc = [];
var index_file_gen = 0;
var index_file_cmdid = 0;
var index_cmdid_gc = 0;
var index_cmdid_gc_out = 0;
function check_gen() {
fs.readdir(folder_proto_gc_gen, function (err, files) {
//handling error
if (err) {
return console.log("Unable to scan directory: " + err);
}
files.forEach(function (file) {
index_file_gen++;
});
console.log("file proto gen: " + index_file_gen);
});
}
// gen json file cmdid
function get_cmdid_json() {
const inputStreamcmdid = fs.createReadStream(read_cmdid);
var lineReadercmdid = readline.createInterface({
input: inputStreamcmdid,
terminal: false,
});
lineReadercmdid.on("line", function (line) {
var config = line.split(",");
var subdata = new Object();
subdata["name"] = config[0];
subdata["id"] = parseInt(config[1]);
data.push(subdata);
index_file_cmdid++;
});
lineReadercmdid.on("close", function () {
console.log("found cmd id " + index_file_cmdid);
save_json(data, read_cmdid_output);
});
}
var data_gen = [];
var index_file_cmdid_gen = 0;
// gen json file cmdid
function read_cmdid_ht_json() {
const k = read_json(read_cmdid_ht_output);
// console.log(k);
for (const key in k) {
var name = k[key];
var id = parseInt(key);
//console.log(id);
var subdata = new Object();
subdata["name"] = name;
subdata["id"] = id;
data_gen.push(subdata);
index_file_cmdid_gen++;
}
console.log("found cmd id " + index_file_cmdid_gen);
save_json(data_gen, read_cmdid_output);
}
// create cmdid from gc which comes from PacketOpcodes
function get_cmdid_gc() {
const inputStreamcmdid = fs.createReadStream(read_cmdid_gc);
var lineReadercmdid = readline.createInterface({
input: inputStreamcmdid,
terminal: false,
});
lineReadercmdid.on("line", function (line) {
var config = line.split(" = ");
var name = config[0];
var id = parseInt(config[1]);
if (name.includes("public static final int")) {
name = name.replace("\tpublic static final int ", "");
// skip 0 ?
if (id == 0) {
return;
}
var subdata = new Object();
subdata["name"] = name;
subdata["id"] = id;
data_gc.push(subdata);
//console.log(name);
index_cmdid_gc++;
} else {
index_cmdid_gc_out++;
}
});
lineReadercmdid.on("close", function () {
console.log(
"found cmd id " + index_cmdid_gc + " | no need " + index_cmdid_gc_out
);
save_json(data_gc, read_cmdid_output_gc);
});
}
var found_cmdid_new = 0;
var nofound_cmdid_new = 0;
var rename_name_cmdid = 0;
var noneed_rename_name_cmdid = 0;
var data_gc_cmdid_nofound = [];
var check_dunp_id = [];
function update_cmdid_gc() {
let melon = "";
// cmdid_gc.json (read_cmdid_output_gc) and cmdid.json (read_cmdid_output)
const json_cmdid_last = read_json(read_cmdid_output);
const json_cmdid_old = read_json(read_cmdid_output_gc);
json_cmdid_old.forEach(function (s, index) {
var id = s.id;
var name = s.name.trim();
// switch to name mode?
var found_id = json_cmdid_last.find((j) => j.name == name);
if (found_id) {
found_cmdid_new++;
if (id == found_id.id) {
noneed_rename_name_cmdid++;
} else {
rename_name_cmdid++;
//console.log("Wow rename -> ID: "+id+" > "+found_id.id);
s.replace = s.id; // old
s.id = found_id.id; // rename id
}
} else {
console.log("Wow nofound -> ID: " + id + " | Name: " + name);
data_gc_cmdid_nofound.push(s);
nofound_cmdid_new++;
}
melon += `${name},${id}\n`;
// find dump by id
/*
var found_id = check_dunp_id.find((j) => j.id == id);
if (found_id) {
console.log("Wow dup -> ID: " +id +" (ADD " +found_id.id +") | Name Remove: " +name +" (ADD " +found_id.name +")");
// remove bad
json_cmdid_old.splice(index, 1);
} else {
check_dunp_id.push(s);
}
*/
// find dump by name?
});
check_dunp_id = []; // clear
// I don't know why this happened but make sure to check again
/*
var check_dunp_name = [];
json_cmdid_old.forEach(function (s, index) {
var id = s.id;
var name = s.name.trim();
var found_name = check_dunp_name.find((j) => j.name === name);
if (found_name) {
console.log(
"Wow dup -> ID: " +
id +
" (ADD " +
found_name.id +
") | Name Remove: " +
name +
" (ADD " +
found_name.name +
")"
);
// remove bad
json_cmdid_old.splice(index, 1);
} else {
check_dunp_name.push(s);
}
var found_id = json_cmdid_last.find((j) => j.id == id);
if (found_id) {
if (name != found_id.name) {
console.log(
"Wow why? -> ID: " + id + " | Name: " + name + " > " + found_id.name
);
s.name = found_id.name;
}
}
});
*/
//check_dunp_name = []; // clear
console.log(
"found " +
found_cmdid_new +
" | no found " +
nofound_cmdid_new +
" | rename " +
rename_name_cmdid +
" | noneed rename " +
noneed_rename_name_cmdid
);
// const csvRows = Object.entries(read_cmdid_output_gc_update).map(([name, id]) => `${name},${id}`);
// const csvData = csvRows.join("\n");
fs.writeFileSync(read_cmdid_last, melon);
save_json(json_cmdid_old, read_cmdid_output_gc_update);
save_json(data_gc_cmdid_nofound, read_cmdid_output_gc_nofound);
}
function read_json(file) {
return JSON.parse(fs.readFileSync(file));
}
// save json
function save_json(raw, file) {
var j = JSON.stringify(raw, null, 4);
save(j, file);
}
function save(raw, file) {
fs.writeFile(file, raw, "utf8", function (err) {
if (err) {
console.log("An error occured while writing to File.");
return console.log(err);
}
console.log("File has been saved: " + file);
});
}
var dup_name = [];
var count_dup = 0;
var count_nodup = 0;
function cmdid_to_op() {
let melon =
"\
package emu.grasscutter.net.packet;\
\n\
\npublic class PacketOpcodes {\
\n// Empty\
\npublic static final int NONE = 0;\
\n\
\n// Opcodes\
";
//const read_file_gcneed = fs.readFileSync(file_gc_needed2);
//const json_gcneed_raw = JSON.parse(read_file_gcneed);
const cmdidfix_raw = fs.readFileSync(read_cmdid_output_gc_update);
const json_cmdidfix_raw = JSON.parse(cmdidfix_raw);
//const json_cmdidfix_raw = read_json(read_cmdid_output_gc_update);
json_cmdidfix_raw.forEach(function (s) {
var found_id = dup_name.find((j) => j.name == s.name);
if (!found_id) {
melon += "\npublic static final int " + s.name + " = " + s.id + ";";
dup_name.push(s);
count_nodup++;
} else {
count_dup++;
if (s.replace) {
console.log("DUP: " + found_id.id + " > " + s.id + " ");
}
}
//console.log(s);
/*
var found_new = json_cmdidfix_raw.find((j) => j.name == s.name);
if(found_new){
}
*/
});
console.log("done no dup " + count_nodup + " | dup " + count_dup);
melon += "\n}";
save(melon, write_op); // use "npx prettier --write PacketOpcodes.java" for better Formatter
}
var index_file_packet = 0;
var index_file_packet_found = 0;
var index_file_packet_nofound = 0;
var index_file_packet_rename = 0;
var index_file_packet_norename = 0;
var index_file_packet_renamemulti = 0;
var file_gc_need = [];
function fix_packet(saveit = false) {
const files = getAllFiles(folder_packet_gc);
const json_cmdid_last = read_json(read_cmdid_output);
const json_cmdid_old = read_json(read_cmdid_output_gc);
const json_cmdidfix_raw = read_json(read_cmdid_output_gc_update);
files.forEach(function (file) {
//var f = path + "/" + file;
const read = fs.readFileSync(file);
var real = read.toString();
var name = getPacketOpcodes(real);
if (!name) {
console.log("no found");
return;
}
if (name === "NONE") {
return;
}
var subdata = new Object();
subdata["name"] = name;
file_gc_need.push(subdata);
//var name = c[1];
//console.log(r);
var found_old = json_cmdid_old.find((j) => j.name === name);
if (found_old) {
//console.log(found_old);
index_file_packet_found++;
var found_new = json_cmdid_last.find((j) => j.id == found_old.id);
if (found_new) {
if (found_new.name != found_old.name) {
index_file_packet_rename++;
console.log(
"Found need rename: " + found_old.name + " > " + found_new.name
);
// rename all
json_cmdidfix_raw.forEach(function (s) {
var r = s.replace;
if (r) {
// var notify = HomeNewUnlockedBgmIdListNotify.Unk2700_MEBFPBDNPGO_ServerNotify
// var notify = Unk2700MEBFPBDNPGOServerNotify.HomeNewUnlockedBgmIdListNotify
// Unk2700MEBFPBDNPGOServerNotify to HomeNewUnlockedBgmIdListNotifyOuterClass
// Unk2700OGHMHELMBNNServerRsp to HomeChangeBgmRspOuterClass
// - Need More Auto like -
// addUnk2700ELJPLMIHNIP to addNewUnlockedBgmIdList (this should be found inside gen proto)
// setUnk2700BJHAMKKECEI to setBgmId
if (r.match("Unk")) {
//console.log(r);
var x = r.split("_");
var tr = x.join("");
var realneed = tr;
if (tr.match("ServerNotify")) {
//console.log("found: "+tr);
let re = new RegExp(`${tr}`, "g");
tr = tr.replace(re, `${s.name}OuterClass`);
//console.log("found: " + tr);
} else if (tr.match("ServerRsp")) {
let re = new RegExp(`${tr}`, "g");
tr = tr.replace(re, `${s.name}OuterClass`);
} else {
let re = new RegExp(`${tr}`, "g");
tr = tr.replace(re, s.name);
//console.log("found: " + tr);
}
let re = new RegExp(`${realneed}`, "g");
real = real.replace(re, tr);
}
let re = new RegExp(`${r}`, "g");
real = real.replace(re, s.name);
}
});
// simpel rename
//let re = new RegExp(`${found_old.name}`, "g");
//real = real.replace(re, found_new.name);
//console.log(real);
if (saveit) {
save(real, f);
}
}
} else {
index_file_packet_norename++;
//console.log("Same name "+name);
}
} else {
index_file_packet_nofound++;
console.log("No found " + name);
}
//return;
index_file_packet++;
});
save_json(file_gc_need, file_gc_needed); // This only applies to PacketOpcodes
console.log(
"Index file: " +
index_file_packet +
" | found " +
index_file_packet_found +
" | No found " +
index_file_packet_nofound +
" | Rename " +
index_file_packet_rename +
" | NoRename " +
index_file_packet_norename
);
}
function isBlank(str) {
return !!!str || /^\s*$/.test(str);
}
function getAllFiles(dirPath, arrayOfFiles) {
files = fs.readdirSync(dirPath);
arrayOfFiles = arrayOfFiles || [];
files.forEach(function (file) {
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles);
} else {
arrayOfFiles.push(path.join(__dirname, dirPath, "/", file));
}
});
return arrayOfFiles;
}
function getPacketOpcodes(raw) {
var r = raw.match(/\(.*?\)/g).map((x) => x.replace(/[()]/g, ""));
var name;
r.forEach(function (s, index) {
if (s.match("PacketOpcodes.")) {
name = s.split("PacketOpcodes.")[1];
if (name.match(",")) {
name = name.split(",")[0];
}
return;
}
});
return name;
}
// C:\Users\Administrator\Desktop\Projek\Docker\GS\gs\GSServer-GC\src\main\java\emu\grasscutter
var file_proto = [];
var file_proto_more = [];
var found_noclean = 0;
var found_needclean = 0;
var found_maybe_related = 0;
const regex_import = /\import "(.*?)\.proto"/g;
// find import in file
function find_import(file) {
var torequire = [];
var dir = path.parse(file).dir;
if (file.match("ModifierDurability")) {
//console.log("fff");
}
var rd;
// read file
try {
const read = fs.readFileSync(file);
rd = read.toString();
} catch (error) {
return;
}
while ((m = regex_import.exec(rd)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex_import.lastIndex) {
regex_import.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
// only index 1 grup
if (groupIndex == 1) {
var found_rt = torequire.find((j) => j === match);
if (found_rt) {
//console.log(`Skip ${match}`);
} else {
if (match.match("Unk")) {
//console.log(file + " require " + match);
}
//var subdata = new Object();
//subdata["name"] = match;
var sub = find_import(dir + "/" + match + ".proto");
if (sub) {
sub.forEach(function (k) {
var v = torequire.find((j) => j === k);
if (!v) {
torequire.push(k);
}
});
}
torequire.push(match);
}
}
});
}
return torequire;
}
function clean_proto_gen() {
//const files = getAllFiles(folder_proto_gc_gen);
const files = getAllFiles("./proto");
const json_gc_needed = read_json(file_gc_needed);
const json_gc_needed2 = read_json(file_gc_needed2);
const json_gc_now = read_json(read_cmdid_output_gc);
const json_gc_update = read_json(read_cmdid_output_gc_update);
//AbilityInvokeArgument
console.log(
"File proto: " + files.length + " | Need " + json_gc_needed.length
);
// find all file import
files.forEach(function (file) {
var name_file = path.parse(file).name;
var toaddfile = new Object();
toaddfile["file"] = name_file;
toaddfile["import"] = find_import(file);
file_proto.push(toaddfile);
});
//console.log(file_proto);
function find_json_proto(name_file, newnew = null) {
var noe = [];
if (newnew) {
noe = newnew;
}
var found_proto = file_proto.find((j) => name_file === j.file); // must same file
if (found_proto) {
// if found import
if (found_proto.import) {
found_proto.import.forEach(function (s) {
var f = noe.find((j) => j === s);
if (!f) {
noe.push(s);
// check sub
var sub = find_json_proto(s, noe);
sub.forEach(function (k) {
var v = noe.find((j) => j === k);
if (!v) {
noe.push(k);
}
});
}
});
}
var ss = noe.find((j) => j === name_file);
if (!ss) {
noe.push(name_file);
}
} else {
// skip
}
return noe;
}
//console.log(find_json_proto("ActivityInfo"));
// try with proto json
var filedonotdelete = [];
files.forEach(function (file) {
var name_file = path.parse(file).name;
var findme = false;
// find main file
var found = json_gc_needed.find((j) => name_file.match(j.name));
if (found) {
findme = true;
} else {
// skip
}
// find miss scan
var found2 = json_gc_needed2.find((j) => name_file.match(j.name));
if (found2) {
findme = true;
} else {
// skip
}
// ModifierDurability >AbilityAppliedModifier -> AbilitySyncStateInfo -> AvatarEnterSceneInfo and multi file
// main file
if (findme) {
var tosub = find_json_proto(name_file);
tosub.forEach(function (k) {
var oo = filedonotdelete.find((j) => j === k);
if (!oo) {
filedonotdelete.push(k);
}
});
} else {
// not main file
/*
if (name_file.match("ModifierDurability")) {
console.log("3");
}
*/
if (file.match("ChannelerSlabChallenge")) {
console.log(file);
}
}
});
// ModifierDurability
//console.log(filedonotdelete);
// last
files.forEach(function (file) {
var name_file = path.parse(file).name;
var toskip = filedonotdelete.find((j) => name_file === j);
if (toskip) {
found_noclean++;
} else {
found_needclean++;
// ActivityInfo
if (file.match("ChannelerSlabChallenge")) {
console.log("ChannelerSlabChallenge");
}
//console.log(name_file);
//console.log("Remove file: " + file);
try {
fs.unlinkSync(file);
//file removed
} catch (err) {
console.error(err);
}
}
});
//console.log(filedonotdelete);
console.log(
"No clean: " +
found_noclean +
" | Need to clean: " +
found_needclean +
" | Related " +
found_maybe_related
);
}
function clean_proto_gen_v2() {
const files = getAllFiles("./proto");
console.log("File proto: " + files.length);
// find all file import
files.forEach(function (file) {
// Get the file name from the file path
const fileName = path.basename(file);
// Use regular expressions to extract the desired part
const match = fileName.match(/^(.*)\.proto$/);
if (match == null) {
return;
}
const messageName = match[1];
// Check if the string is all uppercase
if (messageName === messageName.toUpperCase()) {
console.log("Remove: " + file);
try {
fs.unlinkSync(file);
//file removed
} catch (err) {
console.error(err);
}
} else {
// console.log("String contains non-uppercase letters");
}
});
}
var g_todump = [];
function scan_gc() {
const files = getAllFiles(folder_gc_scan);
console.log("index file: " + files.length);
const regex = /import emu.grasscutter.net.proto.(.*?);/g;
files.forEach(function (file) {
// read file
const read = fs.readFileSync(file);
var rd = read.toString();
// find import
while ((m = regex.exec(rd)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
// only index 1 grup
if (groupIndex == 1) {
if (match === "*") {
return;
}
if (match.match(".")) {
match = match.split(".")[0];
}
match = match.replace("OuterClass", "");
var found_rt = g_todump.find((j) => j.name === match);
if (found_rt) {
//found_maybe_related++;
//console.log(`Skip ${match}`);
} else {
var subdata = new Object();
subdata["name"] = match;
g_todump.push(subdata);
}
}
});
}
});
save_json(g_todump, file_gc_needed2); // This only applies to PacketOpcodes
//console.log(g_todump);
}
function clean_proto_event() {
var torequire = [
"ActivityPushTipsData",
"ActivityWatcherInfo",
"MusicGameActivityDetailInfo",
];
var filedonotdelete = [];
let dir = "./proto";
let file = "./proto/ActivityInfo.proto";
var rd;
const files = getAllFiles(dir);
// read file
try {
const read = fs.readFileSync(file);
rd = read.toString();
} catch (error) {
console.log(error);
return;
}
//console.log(rd);
while ((m = regex_import.exec(rd)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex_import.lastIndex) {
regex_import.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
// only index 1 grup
if (groupIndex == 1) {
//console.log(`t: ${match}`);
var found_rt = torequire.find((j) => j === match);
if (found_rt) {
//console.log(`Skip ${match}`);
} else {
//console.log(`add ${match}`);
var sub = find_import(dir + "/" + match + ".proto");
//console.log(sub);
if (sub) {
sub.forEach(function (k) {
var v = torequire.find((j) => j === k);
if (!v) {
torequire.push(k);
}
});
}
filedonotdelete.push(match);
torequire.push(match);
}
}
});
}
// last
files.forEach(function (file) {
var name_file = path.parse(file).name;
var toskip = filedonotdelete.find((j) => name_file === j);
if (toskip) {
found_needclean++;
//console.log(name_file);
try {
fs.unlinkSync(file);
console.log("Remove file: " + file);
//file removed
} catch (err) {
//console.error(err);
}
} else {
found_noclean++;
}
});
console.log(
"No clean: " +
found_noclean +
" | Need to clean: " +
found_needclean +
" | Related " +
found_maybe_related
);
}
function clean_proto_event_v2() {
var torequire = [
"ParamList",
"StrengthenPointData",
"TowerLevelEndNotify",
"TrialAvatarFirstPassDungeonNotify",
];
var filedonotdelete = [];
let dir = "./proto";
let file = "./proto/DungeonSettleNotify.proto";
var rd;
const files = getAllFiles(dir);
// read file
try {
const read = fs.readFileSync(file);
rd = read.toString();
} catch (error) {
console.log(error);
return;
}
//console.log(rd);
while ((m = regex_import.exec(rd)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex_import.lastIndex) {
regex_import.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
// only index 1 grup
if (groupIndex == 1) {
//console.log(`t: ${match}`);
var found_rt = torequire.find((j) => j === match);
if (found_rt) {
//console.log(`Skip ${match}`);
} else {
//console.log(`add ${match}`);
var sub = find_import(dir + "/" + match + ".proto");
//console.log(sub);
if (sub) {
sub.forEach(function (k) {
var v = torequire.find((j) => j === k);
if (!v) {
torequire.push(k);
}
});
}
filedonotdelete.push(match);
torequire.push(match);
}
}
});
}
console.log(filedonotdelete);
// last
files.forEach(function (file) {
var name_file = path.parse(file).name;
var toskip = filedonotdelete.find((j) => name_file === j);
if (toskip) {
found_needclean++;
console.log(name_file);
try {
fs.unlinkSync(file);
console.log("Remove file: " + file);
//file removed
} catch (err) {
//console.error(err);
}
} else {
found_noclean++;
//console.log("skip "+name_file);
}
});
console.log(
"No clean: " +
found_noclean +
" | Need to clean: " +
found_needclean +
" | Related " +
found_maybe_related
);
}
function cmdid_json_to_csv() {
// read_cmdid_last
const k = read_json(read_cmdid_ht_output);
//console.log(k);
const csvHeaders = "ID,Name\n";
const csvRows = Object.entries(k).map(([name, id]) => `${id},${name}`);
const csvData = csvRows.join("\n");
fs.writeFileSync(read_cmdid_last, csvData);
console.log("CSV file written successfully");
}
// cmdid_json_to_csv();
// Update GC Proto
// get_cmdid_gc(); // 1. get cmd old gc
//read_cmdid_ht_json(); // 2 or
// get_cmdid_json(); // 2. get last cmdid.csv to json
// now we have cmdid_gc.json and cmdid.json
// update_cmdid_gc(); // 3. update gc cmdid (mode by id)
// cmdid_to_op(); // 4. update op
// npx prettier --write PacketOpcodes.java
// scan_gc(); // 5. scan gc
// clean_proto_event(); // 6. clean event, need manual
// clean_proto_gen(); // 6. clean proto
// clean_proto_gen_v2();
// TODO: clean DungeonSettleNotify
// clean_proto_event_v2();