Judge_Engine
The judge module consumes note objects from the core engine and timestamped
inputs from the input engine, aligns them on a shared microsecond clock, and
applies hit/miss rules while emitting callbacks. The snippets below mirror the
tested integration flow in include/tests/JUDGE_TESTS/judgeTest.cpp so that
you can follow the same order of operations.
Runtime Control
-
class JUDGE
Judge controller that owns initialization data and the event loop.
-
enum PDJE_JUDGE::JUDGE_STATUS
Judge runtime status codes.
Values:
-
enumerator OK
-
enumerator CORE_LINE_IS_MISSING
-
enumerator INPUT_LINE_IS_MISSING
-
enumerator EVENT_RULE_IS_EMPTY
-
enumerator INPUT_RULE_IS_EMPTY
-
enumerator NOTE_OBJECT_IS_MISSING
-
enumerator OK
-
JUDGE_STATUS PDJE_JUDGE::JUDGE::Start()
Validate init data and start the judge event loop thread.
-
void PDJE_JUDGE::JUDGE::End()
Stop the event loop and release cached init data.
Start will return an error status when the required init data is missing (core line, input line, note objects, event rule, or input rules). End stops the internal event loop thread and clears the init data for the next run.
Typical order (as used in tests): call
Startafter configuring rail mappings, event rules, custom callbacks, note collection, and wiring both the core and input data lines. On shutdown, callEndafter stopping input and playback.
Initialization
-
class Judge_Init
Judge module initializer holding data lines, rules, and notes.
-
void PDJE_JUDGE::Judge_Init::SetCoreLine(const PDJE_CORE_DATA_LINE &coreline)
Attach the core data line from PDJE core engine.
-
void PDJE_JUDGE::Judge_Init::SetInputLine(const PDJE_INPUT_DATA_LINE &inputline)
Attach the input data line from input engine.
-
void PDJE_JUDGE::Judge_Init::SetRail(const DeviceData &devData, const BITMASK DeviceKey, const int64_t offset_microsecond, const uint64_t MatchRail)
Register an input device rule and its target rail/offset.
-
void PDJE_JUDGE::Judge_Init::SetEventRule(const EVENT_RULE &event_rule)
Set judgment window configuration.
-
void PDJE_JUDGE::Judge_Init::SetCustomEvents(const Custom_Events &events)
Set optional callbacks for miss/use and mouse parsing.
-
void PDJE_JUDGE::Judge_Init::NoteObjectCollector(const std::string noteType, const uint16_t noteDetail, const std::string firstArg, const std::string secondArg, const std::string thirdArg, const unsigned long long Y_Axis, const unsigned long long Y_Axis_2, const uint64_t railID)
Collect note metadata and place it on the matching rail.
SetCoreLine and SetInputLine connect to Data_Lines produced by the core and input engines. Null pointers are ignored.
SetRail registers an INPUT_CONFIG (device id, type, key) and maps it to a rail plus an optional input offset in microseconds.
SetEventRule defines the hit window in microseconds: use_range_microsecond for successful hits and miss_range_microsecond for late/early checks.
SetCustomEvents stores callbacks (see below) and optional sleep times between callback pulls.
NoteObjectCollector fills the internal note buffer. Frame positions (Y_Axis, Y_Axis_2) are converted to microseconds using
Convert_Frame_Into_MicroSecondand routed to the rail that matches the registered input rule. If no device rule exists for the rail, the call is ignored.
Input mapping
SetRail(device, key, offset, rail)is the test-backed helper that binds a discovered device/key to a rail with an optional offset (microseconds). Use this when you want per-device offsets without manually filling a struct.Offsets let you compensate for device latency; negative values advance the input timestamp, positive values delay it.
Rules & Callbacks
-
struct INPUT_CONFIG : public PDJE_JUDGE::DEV, public PDJE_JUDGE::KEY, public PDJE_JUDGE::RAIL, public PDJE_JUDGE::OFFSET
Convenience type combining rule and setting.
-
struct EVENT_RULE
Global hit window configuration in microseconds.
-
struct Custom_Events
Optional callback bundle used during judgment loop.
Custom event callbacks are optional:
missed_event receives a map of rail id -> note vector when notes fall out of the miss window.
used_event receives (rail id, pressed?, late?, diff_microsecond) for each judged input.
custom_mouse_parse is called for mouse axis events with (microsecond, found events, rail id, x, y, axis type).
use_event_sleep_time and miss_event_sleep_time control the pull interval for the two queues (default 100 ms / 200 ms).
Practical callback example (from the integration test):
int miss_count = 0;
PDJE_JUDGE::MISS_CALLBACK missed =
[&miss_count](std::unordered_map<uint64_t, PDJE_JUDGE::NOTE_VEC> misses) {
std::cout << "missed!!!" << miss_count++ << std::endl;
};
PDJE_JUDGE::USE_CALLBACK used = [](uint64_t railid,
bool Pressed,
bool IsLate,
uint64_t diff) {
std::cout << "used!!! " << diff / 1000
<< (IsLate ? " late " : " early ") << std::endl;
};
PDJE_JUDGE::MOUSE_CUSTOM_PARSE_CALLBACK mouse_parse =
[](uint64_t microSecond,
const PDJE_JUDGE::P_NOTE_VEC &found_events,
uint64_t railID,
int x,
int y,
PDJE_Mouse_Axis_Type axis_type) { return; };
judge.inits.SetCustomEvents({
.missed_event = missed,
.used_event = used,
.custom_mouse_parse = mouse_parse,
.use_event_sleep_time = std::chrono::milliseconds(1),
.miss_event_sleep_time = std::chrono::milliseconds(1)
});
In practice, the callbacks are a convenient place to add audio/visual feedback or metrics. Reduce the sleep times (as above) for more responsive feedback.
Integration flow (mirrors test)
// 1) Prepare core and player
PDJE engine{"testRoot.db"};
auto td = engine.SearchTrack("");
engine.InitPlayer(PLAY_MODE::FULL_PRE_RENDER, td.front(), 480);
// 2) Discover input devices and map to rails
PDJE_Input input;
input.Init();
auto devs = input.GetDevs();
DEV_LIST keyboards;
PDJE_JUDGE::JUDGE judge;
for (auto &d : devs) {
if (d.Type == PDJE_Dev_Type::KEYBOARD) {
keyboards.push_back(d);
judge.inits.SetRail(d, PDJE_KEY::A, 0, 1); // device, key, offset, rail
}
}
input.Config(keyboards);
// 3) Collect notes from track data
OBJ_SETTER_CALLBACK cb = [&](const std::string noteType,
const uint16_t noteDetail,
const std::string firstArg,
const std::string secondArg,
const std::string thirdArg,
const unsigned long long Y_Axis,
const unsigned long long Y_Axis_2,
const uint64_t railID) {
judge.inits.NoteObjectCollector(noteType,
noteDetail,
firstArg,
secondArg,
thirdArg,
Y_Axis,
Y_Axis_2,
railID);
};
engine.GetNoteObjects(td.front(), cb);
// 4) Configure event rules and callbacks
judge.inits.SetEventRule({ .miss_range_microsecond = 1'000'005,
.use_range_microsecond = 1'000'000 });
// (See callback example above.)
// 5) Wire data lines
judge.inits.SetInputLine(input.PullOutDataLine());
judge.inits.SetCoreLine(engine.PullOutDataLine());
// 6) Start judge → input → playback (tested order)
if (judge.Start() != PDJE_JUDGE::JUDGE_STATUS::OK) {
std::cerr << "Failed to start judge" << std::endl;
}
input.Run();
engine.player->Activate();
// 7) Shutdown
engine.player->Deactivate();
input.Kill();
judge.End();
#input module init phase
$PDJE_Input_Module.Init()
var device_list:Array = $PDJE_Input_Module.GetDevs()
var selected_devices:Array
for device in device_list:
if device["type"] == "MOUSE":
selected_devices.push_back(device)
print(selected_devices)
$PDJE_Input_Module.Config(selected_devices)
#judge module init phase
$PDJE_Judge_Module.AddDataLines($PDJE_Input_Module, engine)
for dev in selected_devices:
$PDJE_Judge_Module.DeviceAdd(dev, 4, 0, InputLine.BTN_L, 0, 5)#link mouse left button into rail id 5
#if dev's type is keyboard
#$PDJE_Judge_Module.DeviceAdd(dev, InputLine.PDJE_KEY.A, 0, 5)
#$PDJE_Judge_Module.DeviceAdd(dev, InputLine.PDJE_KEY.S, 0, 5)
#$PDJE_Judge_Module.DeviceAdd(dev, InputLine.PDJE_KEY.D, 0, 5)
#$PDJE_Judge_Module.DeviceAdd(dev, InputLine.PDJE_KEY.F, 0, 5)
#link all keyboard's "ASDF" into rail id 5
var use_range= 60 * 1000 # use range +- 60ms
var miss_range= 61 * 1000 # miss range +-61ms
var use_sleep=1 #use evnet thread sleeps 1ms on every loop
var miss_sleep=3 #miss event thread sleeps 3ms on every loop
var custom_mouse_event=false #deactivate mouse custom event. you can use axis data with this.
$PDJE_Judge_Module.SetRule(
use_range,
miss_range,
use_sleep,
miss_sleep,
custom_mouse_event
)# set judge rules
print($PDJE_Judge_Module.SetNotes(engine, "sample_track"))
#Start Game
$PDJE_Judge_Module.StartJudge()
$PDJE_Input_Module.Run()#input module
player.Activate()#core module music player
#Start Judge -> Start Input -> Start Core music player
#End Phase
player.Deactivate()#core module music player
$PDJE_Input_Module.Kill()#input module
$PDJE_Judge_Module.EndJudge()
#End Core music player -> End Input -> End Judge