Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.legendaryforge.legendary.core.api.activation;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterInstance;
import java.util.Optional;
import java.util.UUID;

public record ActivationAttemptResult(
ActivationAttemptStatus status,
ActivationDecision decision,
Optional<UUID> sessionId,
Optional<EncounterInstance> instance) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.legendaryforge.legendary.core.api.activation;

public enum ActivationAttemptStatus {
STARTED,
DENIED,
SESSION_REQUIRED,
FAILED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.legendaryforge.legendary.core.api.activation;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterContext;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterDefinition;
import java.util.UUID;

@FunctionalInterface
public interface ActivationAuthority {

ActivationDecision evaluate(ActivationAuthorityRequest request);

record ActivationAuthorityRequest(UUID activatorId, EncounterDefinition definition, EncounterContext context) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.legendaryforge.legendary.core.api.activation;

import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;

public record ActivationDecision(boolean allowed, ResourceId reasonCode, Map<String, String> attributes) {

public static ActivationDecision allow() {
return new ActivationDecision(true, ResourceId.of("legendarycore", "allowed"), Map.of());
}

public static ActivationDecision deny(ResourceId reasonCode) {
return new ActivationDecision(false, reasonCode, Map.of());
}

public static ActivationDecision deny(ResourceId reasonCode, Map<String, String> attributes) {
return new ActivationDecision(false, reasonCode, Map.copyOf(attributes));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.legendaryforge.legendary.core.api.activation;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterContext;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterDefinition;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Optional;
import java.util.UUID;

public interface ActivationService {

ActivationAttemptResult attemptActivation(ActivationAttemptRequest request);

record ActivationAttemptRequest(
UUID activatorId,
EncounterDefinition definition,
EncounterContext context,
Optional<ResourceId> activationGateKey,
Optional<ActivationAuthority> authorityOverride) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

import io.github.legendaryforge.legendary.core.api.id.ResourceId;

public record ActivationSessionAbortResult(ActivationSessionAbortStatus status, ResourceId reasonCode) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

public enum ActivationSessionAbortStatus {
ABORTED,
ALREADY_ABORTED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.UUID;

public record ActivationSessionBeginResult(
ActivationSessionBeginStatus status, ResourceId reasonCode, UUID sessionId) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

public enum ActivationSessionBeginStatus {
CREATED,
EXISTING,
DENIED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterInstance;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Optional;

public record ActivationSessionCommitResult(
ActivationSessionCommitStatus status, ResourceId reasonCode, Optional<EncounterInstance> instance) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

public enum ActivationSessionCommitStatus {
COMMITTED,
ALREADY_COMMITTED,
DENIED,
FAILED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterContext;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterDefinition;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterKey;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

public interface ActivationSessionService {

ActivationSessionBeginResult begin(ActivationSessionBeginRequest request);

ActivationSessionCommitResult commit(UUID sessionId);

ActivationSessionAbortResult abort(UUID sessionId, ResourceId reasonCode);

Optional<ActivationSessionView> get(UUID sessionId);

record ActivationSessionBeginRequest(
UUID activatorId,
EncounterKey encounterKey,
EncounterDefinition definition,
EncounterContext context,
Optional<ResourceId> activationGateKey,
Map<String, String> attributes) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

public enum ActivationSessionState {
OPEN,
COMMITTED,
ABORTED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.github.legendaryforge.legendary.core.api.activation.session;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterKey;
import java.util.Map;
import java.util.UUID;

public record ActivationSessionView(
UUID sessionId,
UUID activatorId,
EncounterKey encounterKey,
ActivationSessionState state,
Map<String, String> attributes) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.legendaryforge.legendary.core.api.gate;

import io.github.legendaryforge.legendary.core.api.encounter.EncounterKey;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@FunctionalInterface
public interface ConditionGate {

GateDecision evaluate(GateRequest request);

record GateRequest(
ResourceId gateKey,
UUID playerId,
Optional<EncounterKey> encounterKey,
Optional<ResourceId> interactionKind,
Optional<LocationRef> location,
Map<String, String> attributes) {}

record LocationRef(String worldId, int x, int y, int z) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.legendaryforge.legendary.core.api.gate;

import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;

public record GateDecision(boolean allowed, ResourceId reasonCode, Map<String, String> attributes) {

public static GateDecision allow() {
return new GateDecision(true, ResourceId.of("legendarycore", "allowed"), Map.of());
}

public static GateDecision deny(ResourceId reasonCode) {
return new GateDecision(false, reasonCode, Map.of());
}

public static GateDecision deny(ResourceId reasonCode, Map<String, String> attributes) {
return new GateDecision(false, reasonCode, Map.copyOf(attributes));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.legendaryforge.legendary.core.api.gate;

import io.github.legendaryforge.legendary.core.api.id.ResourceId;

public interface GateService {

void register(ResourceId gateKey, ConditionGate gate);

GateDecision evaluate(ConditionGate.GateRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.legendaryforge.legendary.core.internal.activation;

import io.github.legendaryforge.legendary.core.api.activation.ActivationAttemptResult;
import io.github.legendaryforge.legendary.core.api.activation.ActivationAttemptStatus;
import io.github.legendaryforge.legendary.core.api.activation.ActivationDecision;
import io.github.legendaryforge.legendary.core.api.activation.ActivationService;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Optional;

public final class DefaultActivationService implements ActivationService {

@Override
public ActivationAttemptResult attemptActivation(ActivationAttemptRequest request) {
// Intentionally inert until Phase 1 · Step 3 wiring (authority + gate + session + encounter creation).
return new ActivationAttemptResult(
ActivationAttemptStatus.FAILED,
ActivationDecision.deny(ResourceId.of("legendarycore", "not_wired")),
Optional.empty(),
Optional.empty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package io.github.legendaryforge.legendary.core.internal.activation.session;

import io.github.legendaryforge.legendary.core.api.activation.session.*;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterInstance;
import io.github.legendaryforge.legendary.core.api.encounter.EncounterKey;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public final class DefaultActivationSessionService implements ActivationSessionService {

private static final class SessionRecord {
final UUID sessionId;
final UUID activatorId;
final EncounterKey encounterKey;
volatile ActivationSessionState state;
volatile EncounterInstance instance;

SessionRecord(UUID sessionId, UUID activatorId, EncounterKey encounterKey) {
this.sessionId = sessionId;
this.activatorId = activatorId;
this.encounterKey = encounterKey;
this.state = ActivationSessionState.OPEN;
}
}

private final Map<UUID, SessionRecord> sessions = new ConcurrentHashMap<>();
private final Map<EncounterKey, UUID> openSessionsByKey = new ConcurrentHashMap<>();

@Override
public ActivationSessionBeginResult begin(ActivationSessionBeginRequest request) {
UUID existing = openSessionsByKey.get(request.encounterKey());
if (existing != null) {
SessionRecord record = sessions.get(existing);
if (record != null && record.activatorId.equals(request.activatorId())) {
return new ActivationSessionBeginResult(
ActivationSessionBeginStatus.EXISTING,
ResourceId.of("legendarycore", "session_exists"),
existing);
}
return new ActivationSessionBeginResult(
ActivationSessionBeginStatus.DENIED, ResourceId.of("legendarycore", "session_locked"), existing);
}

UUID sessionId = UUID.randomUUID();
SessionRecord record = new SessionRecord(sessionId, request.activatorId(), request.encounterKey());
sessions.put(sessionId, record);
openSessionsByKey.put(request.encounterKey(), sessionId);

return new ActivationSessionBeginResult(
ActivationSessionBeginStatus.CREATED, ResourceId.of("legendarycore", "session_created"), sessionId);
}

@Override
public ActivationSessionCommitResult commit(UUID sessionId) {
SessionRecord record = sessions.get(sessionId);
if (record == null) {
return new ActivationSessionCommitResult(
ActivationSessionCommitStatus.DENIED,
ResourceId.of("legendarycore", "session_not_found"),
Optional.empty());
}

if (record.state == ActivationSessionState.COMMITTED) {
return new ActivationSessionCommitResult(
ActivationSessionCommitStatus.ALREADY_COMMITTED,
ResourceId.of("legendarycore", "already_committed"),
Optional.ofNullable(record.instance));
}

if (record.state == ActivationSessionState.ABORTED) {
return new ActivationSessionCommitResult(
ActivationSessionCommitStatus.DENIED,
ResourceId.of("legendarycore", "session_aborted"),
Optional.empty());
}

record.state = ActivationSessionState.COMMITTED;
openSessionsByKey.remove(record.encounterKey);

return new ActivationSessionCommitResult(
ActivationSessionCommitStatus.COMMITTED, ResourceId.of("legendarycore", "committed"), Optional.empty());
}

@Override
public ActivationSessionAbortResult abort(UUID sessionId, ResourceId reasonCode) {
SessionRecord record = sessions.get(sessionId);
if (record == null || record.state == ActivationSessionState.ABORTED) {
return new ActivationSessionAbortResult(ActivationSessionAbortStatus.ALREADY_ABORTED, reasonCode);
}

record.state = ActivationSessionState.ABORTED;
openSessionsByKey.remove(record.encounterKey);

return new ActivationSessionAbortResult(ActivationSessionAbortStatus.ABORTED, reasonCode);
}

@Override
public Optional<ActivationSessionView> get(UUID sessionId) {
SessionRecord record = sessions.get(sessionId);
if (record == null) return Optional.empty();

return Optional.of(new ActivationSessionView(
record.sessionId, record.activatorId, record.encounterKey, record.state, Map.of()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.github.legendaryforge.legendary.core.internal.gate;

import io.github.legendaryforge.legendary.core.api.gate.ConditionGate;
import io.github.legendaryforge.legendary.core.api.gate.GateDecision;
import io.github.legendaryforge.legendary.core.api.gate.GateService;
import io.github.legendaryforge.legendary.core.api.id.ResourceId;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public final class DefaultGateService implements GateService {

private final Map<ResourceId, ConditionGate> gates = new ConcurrentHashMap<>();

@Override
public void register(ResourceId gateKey, ConditionGate gate) {
gates.put(gateKey, gate);
}

@Override
public GateDecision evaluate(ConditionGate.GateRequest request) {
ConditionGate gate = gates.get(request.gateKey());
if (gate == null) {
return GateDecision.deny(ResourceId.of("legendarycore", "gate_not_registered"));
}
return gate.evaluate(request);
}
}
Loading