mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:55:42 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81838065ae |
@@ -22,11 +22,6 @@ on:
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
# Only allow one deployment at a time to prevent race conditions
|
||||
concurrency:
|
||||
group: docker-build-deploy
|
||||
cancel-in-progress: false # Don't cancel running deployments, queue new ones
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -319,18 +314,32 @@ jobs:
|
||||
|
||||
echo "All images pulled successfully"
|
||||
|
||||
# Recreate shardok (stateless, safe to restart anytime)
|
||||
docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate shardok
|
||||
# Recreate non-Eagle services
|
||||
docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate shardok admin
|
||||
|
||||
# Deploy Eagle with blue-green (handles eagle, nginx, admin, jfr-sidecar)
|
||||
chmod +x /opt/eagle0/scripts/*.sh
|
||||
[ -f "/opt/eagle0/scripts/bin/warmup" ] && chmod +x /opt/eagle0/scripts/bin/warmup
|
||||
GIT_SHA=\$(echo "\${EAGLE_IMAGE}" | sed 's/.*://')
|
||||
/opt/eagle0/scripts/deploy-blue-green.sh "\${GIT_SHA}"
|
||||
# Deploy Eagle with blue-green
|
||||
if [ -x "/opt/eagle0/scripts/deploy-blue-green.sh" ]; then
|
||||
echo "Using blue-green deployment for Eagle..."
|
||||
chmod +x /opt/eagle0/scripts/*.sh
|
||||
[ -f "/opt/eagle0/scripts/bin/warmup" ] && chmod +x /opt/eagle0/scripts/bin/warmup
|
||||
GIT_SHA=\$(echo "\${EAGLE_IMAGE}" | sed 's/.*://')
|
||||
/opt/eagle0/scripts/deploy-blue-green.sh "\${GIT_SHA}" || {
|
||||
echo "Blue-green failed, falling back to direct restart"
|
||||
docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate eagle-blue
|
||||
}
|
||||
else
|
||||
docker compose -f docker-compose.prod.yml up -d --no-deps --force-recreate eagle-blue
|
||||
fi
|
||||
|
||||
# Note: jfr-sidecar is started by deploy-blue-green.sh (either jfr-sidecar or jfr-sidecar-green
|
||||
# depending on which eagle instance is active)
|
||||
|
||||
# Ensure auth is running
|
||||
docker compose -f docker-compose.prod.yml up -d auth
|
||||
|
||||
# Restart nginx
|
||||
docker compose -f docker-compose.prod.yml up -d --force-recreate nginx
|
||||
|
||||
# Verify
|
||||
sleep 10
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
|
||||
@@ -3,9 +3,6 @@ events {
|
||||
}
|
||||
|
||||
http {
|
||||
# Allow large request bodies for game uploads (default is 1MB)
|
||||
client_max_body_size 50M;
|
||||
|
||||
# Logging
|
||||
log_format grpc_json escape=json '{'
|
||||
'"time":"$time_iso8601",'
|
||||
|
||||
@@ -224,12 +224,15 @@ main() {
|
||||
log_warn "s3cmd not found, skipping S3 backup"
|
||||
fi
|
||||
|
||||
# Stop the active instance (flushes state to storage)
|
||||
# We must do this BEFORE switching so staging loads fresh data
|
||||
# Stop the active instance (this flushes state to storage)
|
||||
log_info "Stopping eagle-${active} (flushing state to storage)..."
|
||||
docker compose -f "${COMPOSE_FILE}" stop "eagle-${active}"
|
||||
|
||||
# Prepare nginx config and .env BEFORE restarting anything
|
||||
# Note: No need to explicitly reload games - Eagle uses lazy loading.
|
||||
# Games are loaded on-demand when users reconnect after nginx switches traffic.
|
||||
# This ensures games are always loaded from the freshest storage state.
|
||||
|
||||
# Switch nginx upstream
|
||||
log_info "Switching nginx upstream to eagle-${staging}..."
|
||||
if [ "$staging" = "green" ]; then
|
||||
sed -i.bak 's/eagle-blue:40032/eagle-green:40032/g' "${NGINX_CONF}"
|
||||
@@ -237,6 +240,21 @@ main() {
|
||||
sed -i.bak 's/eagle-green:40032/eagle-blue:40032/g' "${NGINX_CONF}"
|
||||
fi
|
||||
|
||||
# Restart nginx to ensure fresh config is loaded
|
||||
# Note: We use restart instead of reload to ensure all workers pick up the new config
|
||||
log_info "Restarting nginx..."
|
||||
docker compose -f "${COMPOSE_FILE}" restart nginx
|
||||
|
||||
# Clean up old instance and its jfr-sidecar
|
||||
log_info "Removing old eagle-${active} container..."
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "eagle-${active}"
|
||||
if [ "$active" = "green" ]; then
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "jfr-sidecar-green" 2>/dev/null || true
|
||||
else
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "jfr-sidecar" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Update .env with new EAGLE_ADDR and JFR_SIDECAR_ADDR for admin service
|
||||
local env_file="${APP_DIR}/.env"
|
||||
if [ "$staging" = "green" ]; then
|
||||
log_info "Updating .env for green instance..."
|
||||
@@ -250,26 +268,9 @@ main() {
|
||||
echo "JFR_SIDECAR_ADDR=jfr-sidecar:8081" >> "${env_file}"
|
||||
fi
|
||||
|
||||
# Restart nginx (use reload for faster switch, restart if reload fails)
|
||||
log_info "Reloading nginx..."
|
||||
if ! docker exec nginx nginx -s reload 2>/dev/null; then
|
||||
log_warn "Reload failed, restarting nginx..."
|
||||
docker compose -f "${COMPOSE_FILE}" restart nginx
|
||||
fi
|
||||
|
||||
# Restart admin to pick up new .env
|
||||
# Restart admin to pick up new EAGLE_ADDR and JFR_SIDECAR_ADDR
|
||||
log_info "Restarting admin service..."
|
||||
docker compose -f "${COMPOSE_FILE}" up -d --force-recreate admin
|
||||
|
||||
# Clean up old instance and its jfr-sidecar (in background, not critical path)
|
||||
log_info "Cleaning up old eagle-${active} container..."
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "eagle-${active}" &
|
||||
if [ "$active" = "green" ]; then
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "jfr-sidecar-green" 2>/dev/null &
|
||||
else
|
||||
docker compose -f "${COMPOSE_FILE}" rm -f "jfr-sidecar" 2>/dev/null &
|
||||
fi
|
||||
wait
|
||||
docker compose -f "${COMPOSE_FILE}" up -d admin
|
||||
|
||||
log_info "Deployment complete!"
|
||||
log_info "Active instance: eagle-${staging}"
|
||||
|
||||
-32
@@ -109,10 +109,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
|
||||
[Header("Status Display")]
|
||||
public TextMeshProUGUI connectionStatusText;
|
||||
|
||||
[Header("Connection Panel Environment")]
|
||||
[Tooltip("Environment dropdown in connection panel (fallback if lobby unreachable)")]
|
||||
public TMP_Dropdown connectionEnvironmentDropdown;
|
||||
|
||||
public GameObject connectionPanel;
|
||||
public GameObject gameSelectionPanel;
|
||||
public GameObject customBattlePanel;
|
||||
@@ -221,9 +217,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
|
||||
// Initialize OAuth UI
|
||||
SetupOAuthUI();
|
||||
|
||||
// Initialize connection panel environment dropdown
|
||||
SetupConnectionEnvironmentDropdown();
|
||||
|
||||
// Initialize Lobby UI (logout button, etc.)
|
||||
SetupLobbyUI();
|
||||
|
||||
@@ -234,22 +227,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
|
||||
TryRestoreSession();
|
||||
}
|
||||
|
||||
private void SetupConnectionEnvironmentDropdown() {
|
||||
if (connectionEnvironmentDropdown == null) return;
|
||||
|
||||
connectionEnvironmentDropdown.ClearOptions();
|
||||
connectionEnvironmentDropdown.AddOptions(EnvironmentDisplayNames);
|
||||
connectionEnvironmentDropdown.value = PlayerPrefs.GetInt(EnvironmentKey, 0);
|
||||
connectionEnvironmentDropdown.onValueChanged.AddListener(OnConnectionEnvironmentChanged);
|
||||
}
|
||||
|
||||
private void OnConnectionEnvironmentChanged(int newEnvironmentIndex) {
|
||||
// Just save the preference - it will be used on next connection attempt
|
||||
PlayerPrefs.SetInt(EnvironmentKey, newEnvironmentIndex);
|
||||
Debug.Log(
|
||||
$"[ConnectionHandler] Environment changed to {EnvironmentDisplayNames[newEnvironmentIndex]}");
|
||||
}
|
||||
|
||||
private void SetupOAuthUI() {
|
||||
// Set up OAuth button click handlers for new sign-ins
|
||||
if (discordLoginButton != null) {
|
||||
@@ -287,15 +264,6 @@ public class ConnectionHandler : MonoBehaviour, ILobbySubscriber, IDisposable {
|
||||
// Show OAuth panel
|
||||
if (oauthPanel != null) oauthPanel.SetActive(true);
|
||||
|
||||
// Sync connection environment dropdown with saved preference
|
||||
if (connectionEnvironmentDropdown != null) {
|
||||
connectionEnvironmentDropdown.onValueChanged.RemoveListener(
|
||||
OnConnectionEnvironmentChanged);
|
||||
connectionEnvironmentDropdown.value = PlayerPrefs.GetInt(EnvironmentKey, 0);
|
||||
connectionEnvironmentDropdown.onValueChanged.AddListener(
|
||||
OnConnectionEnvironmentChanged);
|
||||
}
|
||||
|
||||
// Refresh stored account buttons
|
||||
RefreshStoredAccountButtons();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
-2
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d58277ecfc2d84d30a7ca71b8ccb994e
|
||||
@@ -129,9 +129,70 @@ function cancelUpload() {
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="/games/{{.GameID}}" role="button" class="outline">View History</a>
|
||||
<button class="btn-small btn-danger" onclick="showDeleteModal('{{.GameID}}')">Delete</button>
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
|
||||
<!-- Delete Game Modal -->
|
||||
<dialog id="delete-game-modal">
|
||||
<form method="dialog">
|
||||
<h3>Delete Game</h3>
|
||||
<p>Are you sure you want to delete game <strong id="delete-game-id-display"></strong>?</p>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="delete-save-files" name="delete_save_files">
|
||||
Also delete save files from disk
|
||||
</label>
|
||||
</div>
|
||||
<p class="warning" style="color: #e74c3c; font-size: 0.9em;">
|
||||
<strong>Warning:</strong> This action cannot be undone.
|
||||
</p>
|
||||
<input type="hidden" id="delete-game-id" name="game_id">
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn-secondary" onclick="document.getElementById('delete-game-modal').close()">Cancel</button>
|
||||
<button type="button" class="btn-danger" onclick="submitDeleteGame()">Delete</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<div id="delete-result"></div>
|
||||
|
||||
<script>
|
||||
function showDeleteModal(gameId) {
|
||||
document.getElementById('delete-game-id').value = gameId;
|
||||
document.getElementById('delete-game-id-display').textContent = gameId;
|
||||
document.getElementById('delete-save-files').checked = false;
|
||||
document.getElementById('delete-game-modal').showModal();
|
||||
}
|
||||
|
||||
function submitDeleteGame() {
|
||||
const gameId = document.getElementById('delete-game-id').value;
|
||||
const deleteSaveFiles = document.getElementById('delete-save-files').checked;
|
||||
|
||||
fetch('/games/' + gameId + '/delete', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: 'delete_save_files=' + (deleteSaveFiles ? 'true' : 'false')
|
||||
}).then(function(response) {
|
||||
if (response.ok) {
|
||||
// Reload page to show updated list
|
||||
window.location.reload();
|
||||
} else {
|
||||
return response.text().then(function(text) {
|
||||
document.getElementById('delete-result').innerHTML =
|
||||
'<div class="alert alert-error">' + text + '</div>';
|
||||
});
|
||||
}
|
||||
}).catch(function(error) {
|
||||
document.getElementById('delete-result').innerHTML =
|
||||
'<div class="alert alert-error">Delete failed: ' + error + '</div>';
|
||||
});
|
||||
|
||||
document.getElementById('delete-game-modal').close();
|
||||
}
|
||||
</script>
|
||||
|
||||
{{else}}
|
||||
<div class="empty-state">
|
||||
<p>No games currently running.</p>
|
||||
|
||||
@@ -58,10 +58,8 @@ message PostCommandResponse {
|
||||
UNKNOWN = 0;
|
||||
SUCCESS = 1;
|
||||
BAD_TOKEN = 2;
|
||||
ERROR = 3;
|
||||
}
|
||||
Status status = 1;
|
||||
string error_message = 2; // Only set if status is ERROR
|
||||
}
|
||||
|
||||
message EnterLobbyRequest {
|
||||
|
||||
@@ -6,10 +6,9 @@ import net.eagle0.eagle.common.action_result_type.ActionResultType.*
|
||||
import net.eagle0.eagle.common.round_phase.RoundPhase
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.actions.impl.common.ActionWithResultingState
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.view_filters.GameStateViewFilter
|
||||
import net.eagle0.eagle.library.util.GameStateViewDiffer
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.views.action_result_view.ActionResultView
|
||||
import net.eagle0.eagle.views.game_state_view.GameStateViewDiff
|
||||
import net.eagle0.eagle.FactionId
|
||||
@@ -107,7 +106,6 @@ object ActionResultFilter {
|
||||
factionId: Option[FactionId]
|
||||
): Option[ActionResultView] = {
|
||||
val actionResult = result.actionResult
|
||||
val factions = startingState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
|
||||
val gsDiff = filteredGameStateDiff(
|
||||
before = startingState,
|
||||
@@ -123,10 +121,10 @@ object ActionResultFilter {
|
||||
factionId.exists { askingFid =>
|
||||
!result.actionResult.player.exists(targetFid =>
|
||||
targetFid == askingFid ||
|
||||
FactionUtils.hasAlliance(
|
||||
LegacyFactionUtils.hasAlliance(
|
||||
askingFid,
|
||||
targetFid,
|
||||
factions
|
||||
startingState
|
||||
)
|
||||
)
|
||||
} &&
|
||||
|
||||
@@ -104,9 +104,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:action_with_resulting_state",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:game_state_view_differ",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/view_filters:game_state_view_filter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+3
-4
@@ -7,9 +7,8 @@ import net.eagle0.eagle.common.diplomacy_offer_status.DiplomacyOfferStatus.{
|
||||
DIPLOMACY_OFFER_STATUS_UNRESOLVED
|
||||
}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.proto_converters.diplomacy_offer.status.StatusConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.state.command.available.AvailableCommand
|
||||
import net.eagle0.eagle.model.state.command.available.AvailableCommand.DiplomacyOfferInfo
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.{DiplomacyOffer, Invitation}
|
||||
@@ -106,8 +105,8 @@ object AvailableResolveInvitationCommandFactory {
|
||||
InvitationDetails(_ /* unknownFieldSet */ ),
|
||||
_ /* unknownFieldSet */
|
||||
) =>
|
||||
val provinces = gameState.provinces.values.map(ProvinceConverter.fromProto)
|
||||
FactionUtils.provinceCount(originatingFactionId, provinces) > 0
|
||||
LegacyFactionUtils
|
||||
.provinceCount(originatingFactionId, gameState) > 0
|
||||
case _ => false
|
||||
}
|
||||
|
||||
|
||||
@@ -628,8 +628,8 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer/status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/command/available",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer/status",
|
||||
@@ -937,7 +937,7 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/diplomacy_offer/status",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
|
||||
+4
-8
@@ -1,8 +1,7 @@
|
||||
package net.eagle0.eagle.library.actions.availability
|
||||
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Imprisoned, Rejected, Status}
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
import net.eagle0.eagle.FactionId
|
||||
@@ -22,10 +21,7 @@ object EligibleDiplomacyStatuses {
|
||||
def maybeImprisonStatus(
|
||||
actingFactionId: FactionId,
|
||||
gameState: GameState
|
||||
): Option[Status] = {
|
||||
val provinces = gameState.provinces.values.map(ProvinceConverter.fromProto)
|
||||
Option.when(
|
||||
FactionUtils.hasProvinces(actingFactionId, provinces)
|
||||
)(Imprisoned)
|
||||
}
|
||||
): Option[Status] = Option.when(
|
||||
LegacyFactionUtils.hasProvinces(actingFactionId, gameState)
|
||||
)(Imprisoned)
|
||||
}
|
||||
|
||||
@@ -1262,6 +1262,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:traveler_to_resident_chance",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:province_distances",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/hero_generation_response",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero_generator/name_info",
|
||||
|
||||
@@ -81,6 +81,7 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:protoless_simple_action",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl/common:t_command",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/quest_fulfillment:quest_fulfillment_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:action_result_trait",
|
||||
"//src/main/scala/net/eagle0/eagle/model/action_result:notification_trait",
|
||||
|
||||
@@ -214,8 +214,8 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:battalion_power",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:battalion_type_id",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
|
||||
@@ -5,9 +5,8 @@ import net.eagle0.eagle.api.command.util.army_stats.ArmyStats
|
||||
import net.eagle0.eagle.internal.army.{Army, MovingArmy}
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.library.util.BattalionPower
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.state.{Army as ArmyC, MovingArmy as MovingArmyC}
|
||||
import net.eagle0.eagle.model.state.game_state.GameState as GameStateC
|
||||
@@ -33,9 +32,8 @@ object IncomingArmyUtils {
|
||||
def incomingArmiesAreMutuallyAllied(
|
||||
province: Province,
|
||||
gameState: GameState
|
||||
): Boolean = {
|
||||
val factions = gameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
FactionUtils.factionsAreMutuallyAllied(
|
||||
): Boolean =
|
||||
LegacyFactionUtils.factionsAreMutuallyAllied(
|
||||
fids = province.incomingArmies
|
||||
.filter(_.arrivalRound == gameState.currentRoundId)
|
||||
.map(_.getArmy.factionId)
|
||||
@@ -43,18 +41,15 @@ object IncomingArmyUtils {
|
||||
.flatMap(_.armies)
|
||||
.map(_.getArmy.factionId)
|
||||
.toVector,
|
||||
factions = factions
|
||||
gs = gameState
|
||||
)
|
||||
}
|
||||
|
||||
def isHostileProvince(
|
||||
province: Province,
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Boolean = {
|
||||
val factions = gameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
province.rulingFactionId.exists(targetFid => FactionUtils.factionsAreHostile(targetFid, factionId, factions))
|
||||
}
|
||||
): Boolean =
|
||||
province.rulingFactionId.exists(targetFid => LegacyFactionUtils.factionsAreHostile(targetFid, factionId, gameState))
|
||||
|
||||
def isUnderAttack(
|
||||
provinceId: ProvinceId,
|
||||
@@ -96,13 +91,12 @@ object IncomingArmyUtils {
|
||||
): ArmyStats = {
|
||||
val battalions = army.units.flatMap(_.battalionId).map(gs.battalions)
|
||||
val troopCount = battalions.map(_.size).sum
|
||||
val factions = gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
|
||||
ArmyStats(
|
||||
factionId = army.factionId,
|
||||
heroCount = army.units.size,
|
||||
troopCount = troopCount,
|
||||
hostility = FactionUtils.hostilityStatus(toFid, army.factionId, factions),
|
||||
hostility = LegacyFactionUtils.hostilityStatus(toFid, army.factionId, gs),
|
||||
originProvinceId = originProvinceId
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,3 +20,20 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
],
|
||||
)
|
||||
|
||||
scala_library(
|
||||
name = "legacy_faction_utils",
|
||||
srcs = ["LegacyFactionUtils.scala"],
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle:__subpackages__",
|
||||
"//src/test/scala/net/eagle0/eagle:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/common:hostility_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_support_for_taxes",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:prestige_per_supported_province",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package net.eagle0.eagle.library.util.faction_utils
|
||||
|
||||
import net.eagle0.common.hostility.Hostility
|
||||
import net.eagle0.eagle.{FactionId, HeroId}
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PrestigePerSupportedProvince}
|
||||
|
||||
/**
|
||||
* Proto-based FactionUtils for code that operates on proto GameState.
|
||||
*
|
||||
* This mirrors the protoless FactionUtils but operates on proto types directly. Use FactionUtils for protoless code;
|
||||
* use this for proto-based boundary code.
|
||||
*/
|
||||
object LegacyFactionUtils {
|
||||
private val sortIgnoredChars: Set[Char] = Set('"', '\u201c', '\u201d', '\'', '\u2018', '\u2019')
|
||||
|
||||
def sortKey(faction: Faction): String =
|
||||
faction.name.filterNot(sortIgnoredChars)
|
||||
|
||||
/** Returns proto FactionRelationship - kept as-is until callers are migrated */
|
||||
def factionRelationship(
|
||||
by: FactionId,
|
||||
of: FactionId,
|
||||
gameState: GameState
|
||||
): FactionRelationship =
|
||||
gameState.factions
|
||||
.get(by)
|
||||
.flatMap(_.factionRelationships.find(_.targetFactionId == of))
|
||||
.getOrElse(
|
||||
FactionRelationship(
|
||||
targetFactionId = of,
|
||||
relationshipLevel = FactionRelationship.RelationshipLevel.HOSTILE,
|
||||
resetDate = None,
|
||||
trustValue = 0
|
||||
)
|
||||
)
|
||||
|
||||
def prestige(factionId: FactionId, gameState: GameState): Double = {
|
||||
val fromSupportedProvinces = supportedProvinceCount(
|
||||
factionId,
|
||||
gameState
|
||||
).toDouble * PrestigePerSupportedProvince.doubleValue
|
||||
|
||||
fromSupportedProvinces + gameState
|
||||
.factions(factionId)
|
||||
.prestigeModifiers
|
||||
.map(_.value)
|
||||
.sum
|
||||
}
|
||||
|
||||
private def supportedProvinceCount(
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Int =
|
||||
gameState.provinces.values
|
||||
.filter(_.rulingFactionId.contains(factionId))
|
||||
.count(_.support >= MinSupportForTaxes.doubleValue)
|
||||
|
||||
def factionHeadId(
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Option[HeroId] =
|
||||
gameState.factions.get(factionId).map(_.factionHeadId)
|
||||
|
||||
// These methods avoid full faction conversion since they only need specific fields
|
||||
def isFactionHead(heroId: HeroId, gameState: GameState): Boolean =
|
||||
gameState.factions.values.map(_.factionHeadId).exists(_ == heroId)
|
||||
|
||||
def leadersBesidesHead(
|
||||
factionId: FactionId,
|
||||
gameState: GameState
|
||||
): Vector[HeroId] = {
|
||||
val faction = gameState.factions(factionId)
|
||||
faction.leaders.filterNot(_ == faction.factionHeadId).toVector
|
||||
}
|
||||
|
||||
def isFactionLeader(heroId: HeroId, gameState: GameState): Boolean =
|
||||
gameState.factions.values.flatMap(_.leaders).exists(_ == heroId)
|
||||
|
||||
/** Returns proto Province - kept as-is until callers are migrated */
|
||||
def provinces(factionId: FactionId, gameState: GameState): Vector[Province] =
|
||||
gameState.provinces.values
|
||||
.filter(_.rulingFactionId.contains(factionId))
|
||||
.toVector
|
||||
|
||||
// These methods avoid full province conversion since they only need rulingFactionId
|
||||
def hasProvinces(factionId: FactionId, gameState: GameState): Boolean =
|
||||
gameState.provinces.values.exists(_.rulingFactionId.contains(factionId))
|
||||
|
||||
def provinceCount(factionId: FactionId, gameState: GameState): Int =
|
||||
gameState.provinces.values.count(_.rulingFactionId.contains(factionId))
|
||||
|
||||
// These methods use factionRelationship directly to avoid full faction conversion
|
||||
// which would fail on incomplete test data (missing DiplomacyOffer fields)
|
||||
def factionsAreMutuallyAllied(
|
||||
fids: Vector[FactionId],
|
||||
gs: GameState
|
||||
): Boolean =
|
||||
fids.forall(fid1 => fids.forall(fid2 => !factionsAreHostile(fid1, fid2, gs)))
|
||||
|
||||
def hasTruce(
|
||||
fid1: FactionId,
|
||||
fid2: FactionId,
|
||||
gs: GameState
|
||||
): Boolean =
|
||||
factionRelationship(fid1, fid2, gs).relationshipLevel ==
|
||||
FactionRelationship.RelationshipLevel.TRUCE
|
||||
|
||||
def hasTruceOrAlliance(
|
||||
fid1: FactionId,
|
||||
fid2: FactionId,
|
||||
gs: GameState
|
||||
): Boolean = hasTruce(fid1, fid2, gs) || hasAlliance(fid1, fid2, gs)
|
||||
|
||||
def hasAlliance(fid1: FactionId, fid2: FactionId, gs: GameState): Boolean =
|
||||
factionRelationship(fid1, fid2, gs).relationshipLevel ==
|
||||
FactionRelationship.RelationshipLevel.ALLY
|
||||
|
||||
def hostilityStatus(
|
||||
fid1: FactionId,
|
||||
fid2: FactionId,
|
||||
gs: GameState
|
||||
): Hostility =
|
||||
if fid1 == fid2 then Hostility.SELF_HOSTILITY
|
||||
else if factionsAreHostile(fid1, fid2, gs) then Hostility.ENEMY_HOSTILITY
|
||||
else Hostility.ALLIED_HOSTILITY
|
||||
|
||||
def factionsAreHostile(
|
||||
fid1: FactionId,
|
||||
fid2: FactionId,
|
||||
gs: GameState
|
||||
): Boolean =
|
||||
fid1 != fid2 && !hasTruceOrAlliance(fid1, fid2, gs)
|
||||
}
|
||||
@@ -38,6 +38,7 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state:army",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province",
|
||||
@@ -78,7 +79,7 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/views:shardok_battle_view_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/shardok_battle",
|
||||
],
|
||||
@@ -104,10 +105,9 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/views:faction_view_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/diplomacy_offer",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/game_state",
|
||||
],
|
||||
@@ -215,13 +215,13 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:province_event_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:stat_with_condition_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/province:legacy_province_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:unaffiliated_hero_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/date",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero:profession",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/province:event",
|
||||
@@ -249,7 +249,7 @@ scala_library(
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/faction",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.eagle0.eagle.{BattalionId, FactionId}
|
||||
import net.eagle0.eagle.internal.army.Army
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.state.{Army as ScalaArmy, MovingArmy}
|
||||
import net.eagle0.eagle.model.state.game_state.GameState as ScalaGameState
|
||||
import net.eagle0.eagle.model.state.province.ProvinceT
|
||||
@@ -86,12 +86,12 @@ object BattalionNameFilter {
|
||||
factionId.map { fid =>
|
||||
val battalionIdsInVisibleProvinces = gs.factions.keys
|
||||
.filter(fid => Visibility.hasFullVisibility(factionId, fid, gs))
|
||||
.flatMap(fid => gs.provinces.values.filter(_.rulingFactionId.contains(fid)))
|
||||
.flatMap(LegacyFactionUtils.provinces(_, gs))
|
||||
.flatMap(_.battalionIds)
|
||||
.toVector
|
||||
|
||||
val battalionIdsIncomingToMyProvinces = gs.provinces.values
|
||||
.filter(_.rulingFactionId.contains(fid))
|
||||
val battalionIdsIncomingToMyProvinces = LegacyFactionUtils
|
||||
.provinces(fid, gs)
|
||||
.flatMap(
|
||||
_.incomingArmies
|
||||
.filter(_.arrivalRound == gs.currentRoundId)
|
||||
|
||||
@@ -3,8 +3,7 @@ package net.eagle0.eagle.library.util.view_filters
|
||||
import net.eagle0.common.hostility.Hostility
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.shardok_battle.ShardokBattle
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.model.state.shardok_battle.ShardokBattle as ScalaShardokBattle
|
||||
import net.eagle0.eagle.views.shardok_battle_view.{ShardokBattlePlayerInfo, ShardokBattleView}
|
||||
@@ -91,17 +90,15 @@ object BattleFilter {
|
||||
battle: ShardokBattle,
|
||||
fid: Option[FactionId],
|
||||
gs: GameState
|
||||
): Vector[ShardokBattlePlayerInfo] = {
|
||||
val factions = gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
): Vector[ShardokBattlePlayerInfo] =
|
||||
battle.players.zipWithIndex.map {
|
||||
case (sp, i) =>
|
||||
ShardokBattlePlayerInfo(
|
||||
playerId = i,
|
||||
eagleFactionId = sp.eagleFid,
|
||||
hostility = fid
|
||||
.map(f => FactionUtils.hostilityStatus(f, sp.eagleFid, factions))
|
||||
.map(f => LegacyFactionUtils.hostilityStatus(f, sp.eagleFid, gs))
|
||||
.getOrElse(Hostility.UNKNOWN_HOSTILITY)
|
||||
)
|
||||
}.toVector
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,9 @@ import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship.RelationshipLevel as RelationshipLevelProto
|
||||
import net.eagle0.eagle.internal.faction_relationship.FactionRelationship as FactionRelationshipProto
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.diplomacy_offer.DiplomacyOfferConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.ProvinceConverter
|
||||
import net.eagle0.eagle.model.state.faction.{FactionRelationship, FactionT}
|
||||
import net.eagle0.eagle.model.state.game_state.GameState as ScalaGameState
|
||||
import net.eagle0.eagle.views.faction_relationship_view.FactionRelationshipView
|
||||
@@ -106,16 +104,14 @@ object FactionViewFilter {
|
||||
faction: Faction,
|
||||
gs: GameState,
|
||||
fid: Option[FactionId]
|
||||
): FactionView = {
|
||||
val scalaFaction = FactionConverter.fromProto(faction)
|
||||
val scalaProvinces = gs.provinces.values.map(ProvinceConverter.fromProto).toVector
|
||||
): FactionView =
|
||||
if fid.isEmpty || Visibility.hasFullVisibility(fid, faction.id, gs) then
|
||||
FactionView(
|
||||
id = faction.id,
|
||||
factionHeadId = faction.factionHeadId,
|
||||
leaders = faction.leaders,
|
||||
name = faction.name,
|
||||
prestige = FactionUtils.prestige(scalaFaction, scalaProvinces).toInt,
|
||||
prestige = LegacyFactionUtils.prestige(faction.id, gs).toInt,
|
||||
factionRelationships = faction.factionRelationships.map(filteredFactionRelationshipView),
|
||||
// You can see *your incoming diplomacy offers on them*, but they cannot
|
||||
incomingDiplomacyOffers = faction.incomingDiplomacyOffers.filter(dof => fid.contains(dof.originatingFactionId)),
|
||||
@@ -130,6 +126,4 @@ object FactionViewFilter {
|
||||
factionRelationships = faction.factionRelationships.map(filteredFactionRelationshipView),
|
||||
incomingDiplomacyOffers = faction.incomingDiplomacyOffers.filter(dof => fid.contains(dof.originatingFactionId))
|
||||
)
|
||||
end if
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ import net.eagle0.eagle.internal.hero.Hero
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.internal.unaffiliated_hero.UnaffiliatedHero
|
||||
import net.eagle0.eagle.library.util.{IncomingArmyUtils, ProvinceEventUtils}
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.library.util.province.LegacyProvinceUtils
|
||||
import net.eagle0.eagle.library.util.province.ProvinceUtils
|
||||
import net.eagle0.eagle.library.util.StatWithConditionUtils.supportScala
|
||||
import net.eagle0.eagle.model.proto_converters.date.DateConverter
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.ProfessionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.province.{ProvinceEventConverter, ProvinceOrderTypeConverter}
|
||||
@@ -396,10 +395,9 @@ object ProvinceViewFilter {
|
||||
factionId: FactionId
|
||||
): ProvinceView = {
|
||||
import net.eagle0.eagle.library.util.DateProtoUtils.ordering
|
||||
val factions = gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
|
||||
val relevantFactions = gs.factions.values.filter(possibleAlly =>
|
||||
FactionUtils.hasAlliance(possibleAlly.id, factionId, factions)
|
||||
LegacyFactionUtils.hasAlliance(possibleAlly.id, factionId, gs)
|
||||
) ++ gs.factions.get(factionId)
|
||||
|
||||
// Take the most recent reconned view from this faction or any allies
|
||||
@@ -624,13 +622,12 @@ object ProvinceViewFilter {
|
||||
gs: GameState
|
||||
): UnaffiliatedHeroBasics = gs.heroes(uh.heroId) match {
|
||||
case hero: Hero =>
|
||||
val factions = gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
UnaffiliatedHeroBasics(
|
||||
heroId = hero.id,
|
||||
nameTextId = hero.nameTextId,
|
||||
profession = ProfessionConverter.fromProto(hero.profession),
|
||||
status = UnaffiliatedHeroConverter.unaffiliatedHeroTypeFromProto(uh.`type`),
|
||||
isFactionLeader = FactionUtils.isFactionLeader(hero.id, factions),
|
||||
isFactionLeader = LegacyFactionUtils.isFactionLeader(hero.id, gs),
|
||||
recruitmentInfo =
|
||||
uh.recruitmentInfo.fold(RecruitmentInfo.Unknown)(UnaffiliatedHeroConverter.recruitmentInfoFromProto)
|
||||
)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package net.eagle0.eagle.library.util.view_filters
|
||||
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.library.util.faction_utils.{FactionUtils, LegacyFactionUtils}
|
||||
import net.eagle0.eagle.model.state.faction.FactionT
|
||||
import net.eagle0.eagle.FactionId
|
||||
|
||||
@@ -36,10 +35,13 @@ object Visibility {
|
||||
fid1: FactionId,
|
||||
fid2: FactionId,
|
||||
gs: GameState
|
||||
): Boolean = {
|
||||
val factions = gs.factions.values.map(FactionConverter.fromProto).toVector
|
||||
fid1 == fid2 || FactionUtils.hasAlliance(fid1, fid2, factions)
|
||||
}
|
||||
): Boolean =
|
||||
fid1 == fid2 || LegacyFactionUtils
|
||||
.hasAlliance(
|
||||
fid1,
|
||||
fid2,
|
||||
gs
|
||||
)
|
||||
|
||||
def hasFullVisibility(
|
||||
ofid1: Option[FactionId],
|
||||
|
||||
+4
-4
@@ -38,7 +38,7 @@ import net.eagle0.eagle.common.province_order_type.ProvinceOrderType as Province
|
||||
import net.eagle0.eagle.model.state.command.common.*
|
||||
import net.eagle0.eagle.model.state.command.selected.*
|
||||
import net.eagle0.eagle.model.state.command.selected.SelectedCommand.*
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Imprisoned, Rejected, Status}
|
||||
import net.eagle0.eagle.model.state.diplomacy_offer.status.{Accepted, Rejected, Status}
|
||||
|
||||
/**
|
||||
* Converts proto SelectedCommand to Scala SelectedCommand.
|
||||
@@ -455,11 +455,11 @@ object SelectedCommandConverter {
|
||||
|
||||
private def statusFromProto(status: DiplomacyOfferStatusProto): Status =
|
||||
status match {
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_ACCEPTED => Accepted
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_REJECTED => Rejected
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_IMPRISONED => Imprisoned
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_ACCEPTED => Accepted
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_REJECTED => Rejected
|
||||
case DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_UNKNOWN |
|
||||
DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_UNRESOLVED |
|
||||
DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_IMPRISONED |
|
||||
DiplomacyOfferStatusProto.DIPLOMACY_OFFER_STATUS_INVALIDATED | DiplomacyOfferStatusProto.Unrecognized(_) =>
|
||||
throw new IllegalArgumentException(s"Invalid resolution status: $status")
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/library:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:__pkg__",
|
||||
"//src/test/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
],
|
||||
exports = [
|
||||
|
||||
@@ -6,7 +6,6 @@ scala_library(
|
||||
visibility = [
|
||||
"//src/main/scala/net/eagle0/eagle/ai:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/availability:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/actions/impl:__subpackages__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__pkg__",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:__subpackages__",
|
||||
|
||||
@@ -84,7 +84,6 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update",
|
||||
"//src/main/scala/net/eagle0/eagle/shardok_interface:battle_update_receiver",
|
||||
"@maven//:com_thesamet_scalapb_scalapb_json4s_3",
|
||||
"@maven//:io_sentry_sentry",
|
||||
"@maven//:org_json4s_json4s_ast_3",
|
||||
"@maven//:org_json4s_json4s_core_3",
|
||||
"@maven//:org_json4s_json4s_native_3",
|
||||
|
||||
@@ -12,7 +12,6 @@ import scala.util.hashing.MurmurHash3
|
||||
import com.google.protobuf.ByteString
|
||||
import io.grpc.stub.StreamObserver
|
||||
import io.grpc.StatusRuntimeException
|
||||
import io.sentry.Sentry
|
||||
import net.eagle0.common.{FunctionalRandom, RandomState, SimpleTimedLogger}
|
||||
import net.eagle0.eagle.api.eagle.*
|
||||
import net.eagle0.eagle.api.eagle.EagleGrpc.Eagle
|
||||
@@ -319,23 +318,6 @@ class EagleServiceImpl(
|
||||
)
|
||||
)
|
||||
)
|
||||
}.recover {
|
||||
case e: Exception =>
|
||||
SimpleTimedLogger.printLogger.logLine(
|
||||
s"Error processing command for ${responseObserver.userName}: ${e.getMessage}"
|
||||
)
|
||||
e.printStackTrace()
|
||||
Sentry.captureException(e)
|
||||
responseObserver.onNext(
|
||||
UpdateStreamResponse(
|
||||
responseDetails = UpdateStreamResponse.ResponseDetails.PostCommandResponse(
|
||||
PostCommandResponse(
|
||||
status = PostCommandResponse.Status.ERROR,
|
||||
errorMessage = e.getMessage
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
()
|
||||
|
||||
|
||||
@@ -153,10 +153,9 @@ scala_library(
|
||||
"//src/main/scala/net/eagle0/common:grpc_retrier",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:vigor_to_constitution_xp_multiplier",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/hero:hero_utils",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters:battalion_converter",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/faction",
|
||||
"//src/main/scala/net/eagle0/eagle/model/proto_converters/hero",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion",
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/battalion/concrete",
|
||||
|
||||
@@ -23,9 +23,8 @@ import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.hero.Hero as EagleHero
|
||||
import net.eagle0.eagle.internal.shardok_battle.{ShardokBattle, ShardokPlayer}
|
||||
import net.eagle0.eagle.library.settings.VigorToConstitutionXpMultiplier
|
||||
import net.eagle0.eagle.library.util.faction_utils.FactionUtils
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.hero.HeroUtils
|
||||
import net.eagle0.eagle.model.proto_converters.faction.FactionConverter
|
||||
import net.eagle0.eagle.model.proto_converters.hero.HeroConverter
|
||||
import net.eagle0.eagle.model.proto_converters.BattalionConverter
|
||||
import net.eagle0.eagle.model.state.unit_status.UnitStatus
|
||||
@@ -370,8 +369,7 @@ class ShardokInterfaceGrpcClient(
|
||||
private def toCommon(
|
||||
armies: Vector[MovingArmy],
|
||||
gameState: GameState
|
||||
): Vector[CommonUnit] = {
|
||||
val factions = gameState.factions.values.map(FactionConverter.fromProto).toVector
|
||||
): Vector[CommonUnit] =
|
||||
armies.flatMap { ma =>
|
||||
ma.getArmy.units
|
||||
.map(EagleUnit.ExpandUnit(_, gameState))
|
||||
@@ -381,12 +379,11 @@ class ShardokInterfaceGrpcClient(
|
||||
canFlee = ma.getArmy.fleeProvinceId.isDefined,
|
||||
canStartFire = HeroUtils.startFireCapable(HeroConverter.fromProto(eu.hero)),
|
||||
canArchery = isArcheryCapable(eu, gameState),
|
||||
isVip = FactionUtils.isFactionLeader(eu.hero.id, factions),
|
||||
isVip = LegacyFactionUtils.isFactionLeader(eu.hero.id, gameState),
|
||||
startingPositionIndex = ma.startingPositionIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def fromCommon(commonUnit: CommonUnit): EagleUnit =
|
||||
EagleUnit(
|
||||
|
||||
@@ -11,3 +11,18 @@ scala_test(
|
||||
"//src/main/scala/net/eagle0/eagle/model/state/province/concrete",
|
||||
],
|
||||
)
|
||||
|
||||
scala_test(
|
||||
name = "legacy_faction_utils_test",
|
||||
srcs = ["LegacyFactionUtilsTest.scala"],
|
||||
deps = [
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:faction_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:game_state_scala_proto",
|
||||
"//src/main/protobuf/net/eagle0/eagle/internal:province_scala_proto",
|
||||
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:min_support_for_taxes",
|
||||
"//src/main/scala/net/eagle0/eagle/library/settings:prestige_per_supported_province",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util:idable",
|
||||
"//src/main/scala/net/eagle0/eagle/library/util/faction_utils:legacy_faction_utils",
|
||||
],
|
||||
)
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package net.eagle0.eagle.library.util.faction_utils
|
||||
|
||||
import net.eagle0.eagle.internal.faction.Faction
|
||||
import net.eagle0.eagle.internal.game_state.GameState
|
||||
import net.eagle0.eagle.internal.province.Province
|
||||
import net.eagle0.eagle.library.settings.{MinSupportForTaxes, PrestigePerSupportedProvince}
|
||||
import net.eagle0.eagle.library.util.faction_utils.LegacyFactionUtils
|
||||
import net.eagle0.eagle.library.util.IDable.{mapifyFactions, mapifyProvinces}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
|
||||
class LegacyFactionUtilsTest extends AnyFlatSpec with Matchers with BeforeAndAfterEach {
|
||||
|
||||
behavior of "FactionUtilsTest"
|
||||
|
||||
override def beforeEach(): Unit = {
|
||||
PrestigePerSupportedProvince.setDoubleValue(8)
|
||||
MinSupportForTaxes.setDoubleValue(40)
|
||||
}
|
||||
|
||||
private val gameState = GameState(
|
||||
factions = mapifyFactions(
|
||||
Faction(id = 5, factionHeadId = 10, leaders = Vector(10, 20, 15, 25)),
|
||||
Faction(id = 7, factionHeadId = 14),
|
||||
Faction(id = 12, factionHeadId = 24)
|
||||
),
|
||||
provinces = mapifyProvinces(
|
||||
Province(
|
||||
id = 1,
|
||||
rulingFactionId = Some(5),
|
||||
support = 60,
|
||||
rulingFactionHeroIds = Vector(15, 16)
|
||||
),
|
||||
Province(
|
||||
id = 2,
|
||||
rulingFactionId = Some(5),
|
||||
support = 39,
|
||||
rulingFactionHeroIds = Vector(10, 11)
|
||||
),
|
||||
Province(id = 3, rulingFactionId = Some(7), support = 5),
|
||||
Province(id = 4),
|
||||
Province(id = 5, rulingFactionId = Some(12), support = 38),
|
||||
Province(id = 6, rulingFactionId = Some(7), support = 50),
|
||||
Province(
|
||||
id = 7,
|
||||
rulingFactionId = Some(5),
|
||||
support = 41,
|
||||
rulingFactionHeroIds = Vector(20)
|
||||
),
|
||||
Province(
|
||||
id = 8,
|
||||
rulingFactionId = Some(5),
|
||||
support = 100,
|
||||
rulingFactionHeroIds = Vector(25)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
it should "calculate prestige based on number of supporting provinces" in {
|
||||
LegacyFactionUtils.prestige(5, gameState) shouldBe 24
|
||||
LegacyFactionUtils.prestige(7, gameState) shouldBe 8
|
||||
LegacyFactionUtils.prestige(12, gameState) shouldBe 0
|
||||
}
|
||||
|
||||
"isFactionLeader" should "return true if the hero is a faction leader" in {
|
||||
val gs = GameState(
|
||||
factions = mapifyFactions(Faction(id = 3, leaders = Vector(9, 10)))
|
||||
)
|
||||
|
||||
LegacyFactionUtils.isFactionLeader(10, gs) shouldBe true
|
||||
}
|
||||
|
||||
it should "return false if the hero is not a faction leader" in {
|
||||
val gs = GameState(
|
||||
factions = Map(3 -> Faction(id = 3, leaders = Vector(9, 10)))
|
||||
)
|
||||
|
||||
LegacyFactionUtils.isFactionLeader(11, gs) shouldBe false
|
||||
}
|
||||
|
||||
"isFactionHead" should "return true if the hero is a faction head" in {
|
||||
val gs = GameState(
|
||||
factions = mapifyFactions(
|
||||
Faction(id = 3, factionHeadId = 10, leaders = Vector(9, 10))
|
||||
)
|
||||
)
|
||||
|
||||
LegacyFactionUtils.isFactionHead(10, gs) shouldBe true
|
||||
}
|
||||
|
||||
it should "return false if the hero is not a faction head" in {
|
||||
val gs = GameState(
|
||||
factions = mapifyFactions(
|
||||
Faction(id = 3, factionHeadId = 9, leaders = Vector(9, 10))
|
||||
)
|
||||
)
|
||||
|
||||
LegacyFactionUtils.isFactionHead(10, gs) shouldBe false
|
||||
}
|
||||
|
||||
"sortKey" should "return the name for a normal string" in {
|
||||
val faction = Faction(name = "Joe")
|
||||
|
||||
LegacyFactionUtils.sortKey(faction) shouldBe "Joe"
|
||||
}
|
||||
|
||||
it should "exclude quotes" in {
|
||||
val faction = Faction(name = "\"Mad\" Mal")
|
||||
|
||||
LegacyFactionUtils.sortKey(faction) shouldBe "Mad Mal"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user