fuck it
This commit is contained in:
@@ -0,0 +1,332 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="8255b4cc-71e1-4e04-ad77-2f8db881317c" name="Changes" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/commands/ClientCommand.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/commands/ExecuteScriptCommand.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/commands/ExitCommand.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/commands/HelpCommand.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/commands/UndefinedCommand.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/client/src/main/kotlin/runner/ModelValidator.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/common/src/main/kotlin/commands/Command.kt" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/common/src/main/kotlin/network/CommandInfo.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/build.gradle" beforeDir="false" afterPath="$PROJECT_DIR$/client/build.gradle" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/src/main/kotlin/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/main/kotlin/Main.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/src/main/kotlin/input/HumanBeingBuilder.kt" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/main/kotlin/input/HumanBeingBuilder.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/src/main/kotlin/input/IOManager.kt" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/main/kotlin/input/IOManager.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/src/main/kotlin/network/NetworkManager.kt" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/main/kotlin/network/NetworkManager.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client/src/main/kotlin/runner/CommandValidator.kt" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/common/src/main/kotlin/models/HumanBeing.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/models/HumanBeing.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/common/src/main/kotlin/network/AppJson.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/network/AppJson.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/common/src/main/kotlin/network/Response.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/network/Response.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/common/src/main/kotlin/network/utils/LocalDateSerializer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/network/utils/LocalDateSerializer.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/common/src/main/kotlin/network/utils/UUIDSerializer.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/network/utils/UUIDSerializer.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/Main.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/Main.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/collection/CollectionManager.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/collection/CollectionManager.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddIfMaxCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddIfMaxCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddIfMin.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/AddIfMin.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/ClearCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/ClearCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/Command.kt" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/CommandType.kt" beforeDir="false" afterPath="$PROJECT_DIR$/common/src/main/kotlin/commands/CommandType.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/ExitCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/ExitCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/GetCommandsCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/SyncCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/HelpCommand.kt" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/HistoryCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/HistoryCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/InfoCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/InfoCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/MinByNameCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/MinByNameCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/PrintDescendingMinutesCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/PrintDescendingMinutesCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/RemoveByIdCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/RemoveByIdCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/SaveCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/SaveCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/ShowCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/ShowCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/SumOfMinutesCommands.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/SumOfMinutesCommands.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/commands/UpdateCommand.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/commands/UpdateCommand.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/file/FileManager.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/file/FileManager.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/network/NetworkManager.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/network/NetworkManager.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/kotlin/runner/CommandInvoker.kt" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/kotlin/runner/CommandInvoker.kt" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/server/src/main/resources/logback.xml" beforeDir="false" afterPath="$PROJECT_DIR$/server/src/main/resources/logback.xml" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="ExternalProjectsData">
|
||||||
|
<projectState path="$PROJECT_DIR$">
|
||||||
|
<ProjectState />
|
||||||
|
</projectState>
|
||||||
|
</component>
|
||||||
|
<component name="ExternalProjectsManager">
|
||||||
|
<system id="GRADLE">
|
||||||
|
<state>
|
||||||
|
<task path="$PROJECT_DIR$">
|
||||||
|
<activation />
|
||||||
|
</task>
|
||||||
|
<task path="$PROJECT_DIR$/server">
|
||||||
|
<activation />
|
||||||
|
</task>
|
||||||
|
<task path="$PROJECT_DIR$/client">
|
||||||
|
<activation />
|
||||||
|
</task>
|
||||||
|
<projects_view>
|
||||||
|
<tree_state>
|
||||||
|
<expand>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||||
|
<item name="build" type="c8890929:TasksNode$1" />
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
<item name="client" type="2d1252cf:ModuleNode" />
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
<item name="client" type="2d1252cf:ModuleNode" />
|
||||||
|
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
|
||||||
|
<item name="Lab6" type="f1a62948:ProjectNode" />
|
||||||
|
<item name="client" type="2d1252cf:ModuleNode" />
|
||||||
|
<item name="Tasks" type="e4a08cd1:TasksNode" />
|
||||||
|
<item name="build" type="c8890929:TasksNode$1" />
|
||||||
|
</path>
|
||||||
|
</expand>
|
||||||
|
<select />
|
||||||
|
</tree_state>
|
||||||
|
</projects_view>
|
||||||
|
</state>
|
||||||
|
</system>
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="Kotlin File" />
|
||||||
|
<option value="Kotlin Object" />
|
||||||
|
<option value="Kotlin Interface" />
|
||||||
|
<option value="Class" />
|
||||||
|
<option value="Kotlin Class" />
|
||||||
|
<option value="Kotlin Enum" />
|
||||||
|
<option value="Kotlin Data Class" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="GitHubPullRequestSearchHistory">{
|
||||||
|
"lastFilter": {
|
||||||
|
"state": "OPEN",
|
||||||
|
"assignee": "notmewhyitsalwaysme"
|
||||||
|
}
|
||||||
|
}</component>
|
||||||
|
<component name="GithubPullRequestsUISettings">{
|
||||||
|
"selectedUrlAndAccountId": {
|
||||||
|
"url": "git@github.com:notmewhyitsalwaysme/Lab6-prog.git",
|
||||||
|
"accountId": "ffaeab48-780d-4ef5-b729-5e08294300ef"
|
||||||
|
}
|
||||||
|
}</component>
|
||||||
|
<component name="ProjectColorInfo">{
|
||||||
|
"associatedIndex": 2
|
||||||
|
}</component>
|
||||||
|
<component name="ProjectId" id="3CDaGKZTGiGUMJBmB35RQmnLv2b" />
|
||||||
|
<component name="ProjectLevelVcsManager">
|
||||||
|
<ConfirmationsSetting value="2" id="Add" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent">{
|
||||||
|
"keyToString": {
|
||||||
|
"Gradle.Build Lab6.executor": "Run",
|
||||||
|
"Gradle.Lab6 [build].executor": "Run",
|
||||||
|
"Gradle.Lab6 [shadowJar].executor": "Run",
|
||||||
|
"Gradle.Lab6:client [build].executor": "Run",
|
||||||
|
"Gradle.Lab6:server [build].executor": "Run",
|
||||||
|
"Kotlin.MainKt (1).executor": "Run",
|
||||||
|
"Kotlin.MainKt.executor": "Run",
|
||||||
|
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
|
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
|
||||||
|
"git-widget-placeholder": "main",
|
||||||
|
"last_opened_file_path": "/Users/aklivtsov/Projects/Прог/Lab6/server",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"nodejs_package_manager_path": "npm",
|
||||||
|
"onboarding.tips.debug.path": "/Users/aklivtsov/Projects/Прог/Lab6/src/main/kotlin/Main.kt",
|
||||||
|
"project.structure.last.edited": "Modules",
|
||||||
|
"project.structure.proportion": "0.0",
|
||||||
|
"project.structure.side.proportion": "0.0",
|
||||||
|
"settings.editor.selected.configurable": "preferences.keymap",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
}
|
||||||
|
}</component>
|
||||||
|
<component name="RecentsManager">
|
||||||
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/server" />
|
||||||
|
<recent name="$PROJECT_DIR$" />
|
||||||
|
</key>
|
||||||
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="$PROJECT_DIR$/client/build/libs" />
|
||||||
|
<recent name="$PROJECT_DIR$/client/build" />
|
||||||
|
<recent name="$PROJECT_DIR$/client" />
|
||||||
|
</key>
|
||||||
|
<key name="K2MoveDeclarationsDialog.RECENT_PACKAGE_KEY">
|
||||||
|
<recent name="commands" />
|
||||||
|
</key>
|
||||||
|
</component>
|
||||||
|
<component name="RunManager" selected="Gradle.Lab6 [build]">
|
||||||
|
<configuration name="Lab6 [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="build" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>false</RunAsTest>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="Lab6:client [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/client" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="build" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>false</RunAsTest>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="Lab6:server [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/server" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="build" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>false</RunAsTest>
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration name="MainKt (1)" type="JetRunConfigurationType" temporary="true" nameIsGenerated="true">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="lab5.prog.MainKt" />
|
||||||
|
<module name="Lab6.client.main" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration name="MainKt" type="JetRunConfigurationType" temporary="true" nameIsGenerated="true">
|
||||||
|
<option name="MAIN_CLASS_NAME" value="MainKt" />
|
||||||
|
<module name="Lab6.server.main" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<recent_temporary>
|
||||||
|
<list>
|
||||||
|
<item itemvalue="Gradle.Lab6 [build]" />
|
||||||
|
<item itemvalue="Gradle.Lab6:client [build]" />
|
||||||
|
<item itemvalue="Gradle.Lab6:server [build]" />
|
||||||
|
<item itemvalue="Kotlin.MainKt (1)" />
|
||||||
|
<item itemvalue="Kotlin.MainKt" />
|
||||||
|
</list>
|
||||||
|
</recent_temporary>
|
||||||
|
</component>
|
||||||
|
<component name="SharedIndexes">
|
||||||
|
<attachedChunks>
|
||||||
|
<set>
|
||||||
|
<option value="bundled-jdk-9823dce3aa75-fbdcb00ec9e3-intellij.indexing.shared.core-IU-251.26094.121" />
|
||||||
|
<option value="bundled-js-predefined-d6986cc7102b-b26f3e71634d-JavaScript-IU-251.26094.121" />
|
||||||
|
</set>
|
||||||
|
</attachedChunks>
|
||||||
|
</component>
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="Default task">
|
||||||
|
<changelist id="8255b4cc-71e1-4e04-ad77-2f8db881317c" name="Changes" comment="" />
|
||||||
|
<created>1775922497640</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1775922497640</updated>
|
||||||
|
<workItem from="1775922498637" duration="1487000" />
|
||||||
|
<workItem from="1775925490281" duration="35000" />
|
||||||
|
<workItem from="1775926686698" duration="2873000" />
|
||||||
|
<workItem from="1776006672050" duration="17531000" />
|
||||||
|
<workItem from="1776083371625" duration="7547000" />
|
||||||
|
<workItem from="1776169079658" duration="2439000" />
|
||||||
|
<workItem from="1776370566070" duration="1055000" />
|
||||||
|
<workItem from="1776425446066" duration="15604000" />
|
||||||
|
<workItem from="1776496828085" duration="645000" />
|
||||||
|
<workItem from="1776507809946" duration="5019000" />
|
||||||
|
<workItem from="1776792937822" duration="18000" />
|
||||||
|
<workItem from="1776793025237" duration="4361000" />
|
||||||
|
<workItem from="1776849171223" duration="9126000" />
|
||||||
|
<workItem from="1776926840347" duration="191000" />
|
||||||
|
<workItem from="1777054819548" duration="2281000" />
|
||||||
|
<workItem from="1777137270484" duration="466000" />
|
||||||
|
<workItem from="1777365159796" duration="1511000" />
|
||||||
|
<workItem from="1777387591468" duration="598000" />
|
||||||
|
<workItem from="1777488835350" duration="7461000" />
|
||||||
|
<workItem from="1777498959887" duration="358000" />
|
||||||
|
<workItem from="1777581645459" duration="2094000" />
|
||||||
|
<workItem from="1777708062296" duration="6811000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -23,7 +23,11 @@ shadowJar {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
||||||
|
implementation 'io.github.microutils:kotlin-logging-jvm:3.0.5'
|
||||||
implementation "org.jline:jline:3.30.6"
|
implementation "org.jline:jline:3.30.6"
|
||||||
|
implementation "io.github.oshai:kotlin-logging-jvm:7.0.3"
|
||||||
|
implementation "org.slf4j:slf4j-api:2.0.13"
|
||||||
|
implementation 'ch.qos.logback:logback-classic:1.5.13'
|
||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
import ch.qos.logback.core.net.server.Client
|
||||||
|
import commands.ExecuteScriptCommand
|
||||||
|
import commands.ExitCommand
|
||||||
|
import commands.HelpCommand
|
||||||
|
import commands.SyncCommand
|
||||||
|
import commands.UndefinedCommand
|
||||||
import network.NetworkManager
|
import network.NetworkManager
|
||||||
import input.HumanBeingBuilder
|
|
||||||
import input.IOManager
|
import input.IOManager
|
||||||
import network.Request
|
import network.Request
|
||||||
import runner.CommandValidator
|
import runner.CommandInvoker
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val host = if (args.isNotEmpty()) args[0] else "localhost"
|
val host = if (args.isNotEmpty()) args[0] else "localhost"
|
||||||
@@ -10,7 +15,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
val io = IOManager() // Name by Boris Bosenko
|
val io = IOManager() // Name by Boris Bosenko
|
||||||
val client = NetworkManager(host, port)
|
val client = NetworkManager(host, port)
|
||||||
val validator = CommandValidator()
|
val invoker = CommandInvoker(io)
|
||||||
|
|
||||||
// Shutdown hook
|
// Shutdown hook
|
||||||
Runtime.getRuntime().addShutdownHook(Thread {
|
Runtime.getRuntime().addShutdownHook(Thread {
|
||||||
@@ -23,70 +28,32 @@ fun main(args: Array<String>) {
|
|||||||
|════════════════════════════════════════════
|
|════════════════════════════════════════════
|
||||||
""".trimMargin())
|
""".trimMargin())
|
||||||
|
|
||||||
runClientRepl(io, client, validator)
|
registerBasicCommands(invoker, client, io)
|
||||||
|
runClientRepl(io, invoker)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runClientRepl(io: IOManager, client: NetworkManager, validator: CommandValidator) {
|
fun registerBasicCommands(invoker: CommandInvoker,client: NetworkManager, io: IOManager) {
|
||||||
|
listOf(
|
||||||
|
HelpCommand(invoker),
|
||||||
|
ExecuteScriptCommand(io),
|
||||||
|
SyncCommand(invoker, client)
|
||||||
|
).forEach { invoker.register(it)}
|
||||||
|
}
|
||||||
|
|
||||||
val initialRequest = Request(
|
fun runClientRepl(io: IOManager, invoker : CommandInvoker) {
|
||||||
commandName = "getCommandsCommand",
|
// sync commands w server
|
||||||
humanBeing = null
|
invoker.execute("sync")
|
||||||
)
|
|
||||||
|
|
||||||
if (client.sendRequest(initialRequest) == null) {
|
var isRunning = true
|
||||||
io.print("[!] Сервер недоступен. Попробуйте позже.")
|
invoker.register(ExitCommand {isRunning = false})
|
||||||
} else {
|
|
||||||
io.print("|Введите 'help' для справки.")
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
while (isRunning) {
|
||||||
val line = io.readLine(" ☭ ") ?: break
|
val line = io.readLine(" ☭ ") ?: break
|
||||||
if (line.isBlank()) continue
|
if (line.isBlank()) continue
|
||||||
|
invoker.execute(line)
|
||||||
val parts = line.trim().split(" ")
|
|
||||||
val commandName = parts[0].lowercase()
|
|
||||||
val args = parts.drop(1)
|
|
||||||
|
|
||||||
val (isValidCommand, message) = validator.validate(commandName, args)
|
|
||||||
|
|
||||||
if (!isValidCommand) {
|
|
||||||
if (message != null) io.print(message)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandName == "exit") {
|
|
||||||
io.print("До свидания!")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandName == "execute_script") {
|
|
||||||
io.addScriptScanner(args[0])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val humanBeing = if (validator.isBuildsHumanBeing(commandName)) {
|
|
||||||
try {
|
|
||||||
HumanBeingBuilder(io).build()
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
io.print("[Ошибка] Ввод прерван: ${e.message}")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else null
|
|
||||||
|
|
||||||
// Формируем запрос и отправляем
|
|
||||||
val request = Request(
|
|
||||||
commandName = commandName,
|
|
||||||
args = args,
|
|
||||||
humanBeing = humanBeing
|
|
||||||
)
|
|
||||||
|
|
||||||
val response = client.sendRequest(request)
|
|
||||||
|
|
||||||
// Обрабатываем ответ
|
|
||||||
if (response == null) {
|
|
||||||
io.print("[!] Сервер недоступен. Попробуйте позже.")
|
|
||||||
} else {
|
|
||||||
io.print(response.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// and here we exit that crazy circus of code and tears
|
||||||
|
io.print("До свидания!")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import models.HumanBeing
|
||||||
|
|
||||||
|
interface ClientCommand {
|
||||||
|
val name: String
|
||||||
|
val description: String
|
||||||
|
val args: List<CommandArgType>
|
||||||
|
val type: CommandType
|
||||||
|
|
||||||
|
fun execute(args: List<String>, humanBeing: HumanBeing? = null): String
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import input.IOManager
|
||||||
|
import models.HumanBeing
|
||||||
|
|
||||||
|
class ExecuteScriptCommand(private val io: IOManager) : ClientCommand {
|
||||||
|
override val name = "execute_script"
|
||||||
|
override val description = "Выполняет скрипт из файла"
|
||||||
|
override val args = listOf(CommandArgType.STRING)
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
|
||||||
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||||
|
io.addScriptScanner(args[0])
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import models.HumanBeing
|
||||||
|
|
||||||
|
class ExitCommand(private val onExit: () -> Unit) : ClientCommand {
|
||||||
|
override val name = "exit"
|
||||||
|
override val description = "завершить клиент"
|
||||||
|
override val args: List<CommandArgType> = listOf(CommandArgType.NONE)
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
|
||||||
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||||
|
onExit()
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-3
@@ -1,15 +1,16 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import runner.CommandInvoker
|
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import runner.CommandInvoker
|
||||||
|
|
||||||
class HelpCommand(private val invoker: CommandInvoker) : Command {
|
class HelpCommand(private val invoker: CommandInvoker) : ClientCommand {
|
||||||
override val name = "help"
|
override val name = "help"
|
||||||
override val description = "вывести справку по доступным командам"
|
override val description = "вывести справку по доступным командам"
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
||||||
invoker.getCommands().values
|
invoker.getCommands().values
|
||||||
.sortedBy { it.name }
|
.sortedBy { it.name }
|
||||||
.joinToString("\n") { " ${it.name.padEnd(40)} — ${it.description}" }
|
.joinToString("\n") { " ${it.name.padEnd(40)} — ${it.description}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import input.IOManager
|
||||||
|
import models.HumanBeing
|
||||||
|
import network.NetworkManager
|
||||||
|
import network.Request
|
||||||
|
import runner.CommandInvoker
|
||||||
|
|
||||||
|
class SyncCommand (
|
||||||
|
private val invoker: CommandInvoker,
|
||||||
|
private val client: NetworkManager,
|
||||||
|
) : ClientCommand {
|
||||||
|
override val name = "sync"
|
||||||
|
override val description = "получить команды с сервера"
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
|
||||||
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||||
|
val initialRequest = Request(
|
||||||
|
commandName = "sync",
|
||||||
|
humanBeing = null
|
||||||
|
)
|
||||||
|
val res = client.sendRequest(initialRequest)
|
||||||
|
|
||||||
|
if (res == null) {
|
||||||
|
return ("[!] Сервер недоступен. Попробуйте позже.")
|
||||||
|
} else {
|
||||||
|
res.commands?.forEach { fuckingCommand ->
|
||||||
|
invoker.register(
|
||||||
|
UndefinedCommand( // ser
|
||||||
|
name = fuckingCommand.name,
|
||||||
|
description = fuckingCommand.description,
|
||||||
|
type = fuckingCommand.type,
|
||||||
|
args = fuckingCommand.args,
|
||||||
|
networkManager = client
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return ("| Подключен. | Введите 'help' для справки.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import input.HumanBeingBuilder
|
||||||
|
import models.HumanBeing
|
||||||
|
import network.Request
|
||||||
|
import network.NetworkManager
|
||||||
|
|
||||||
|
class UndefinedCommand(
|
||||||
|
override var name: String,
|
||||||
|
override var description: String,
|
||||||
|
override var args: List<CommandArgType>,
|
||||||
|
override var type: CommandType,
|
||||||
|
private val networkManager: NetworkManager,
|
||||||
|
): ClientCommand {
|
||||||
|
|
||||||
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||||
|
val request = Request(
|
||||||
|
commandName = this.name,
|
||||||
|
args = args,
|
||||||
|
humanBeing = humanBeing
|
||||||
|
)
|
||||||
|
val response = networkManager.sendRequest(request)
|
||||||
|
return response?.message ?: "[!] Сервер не ответил"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import models.WeaponType
|
|||||||
*
|
*
|
||||||
* @property inputManager источник ввода
|
* @property inputManager источник ввода
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class HumanBeingBuilder(private val inputManager: IOManager) {
|
class HumanBeingBuilder(private val inputManager: IOManager) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,16 +6,13 @@ import org.jline.reader.LineReaderBuilder
|
|||||||
import org.jline.reader.UserInterruptException
|
import org.jline.reader.UserInterruptException
|
||||||
import org.jline.reader.impl.history.DefaultHistory
|
import org.jline.reader.impl.history.DefaultHistory
|
||||||
import org.jline.terminal.TerminalBuilder
|
import org.jline.terminal.TerminalBuilder
|
||||||
import org.jline.reader.impl.completer.StringsCompleter
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.util.ArrayDeque
|
import java.util.ArrayDeque
|
||||||
import java.util.Scanner
|
import java.util.Scanner
|
||||||
import runner.CommandValidator
|
|
||||||
|
|
||||||
class IOManager {
|
class IOManager {
|
||||||
val isInteractive: Boolean = true
|
val isInteractive: Boolean = true
|
||||||
val commandValidator: CommandValidator = CommandValidator()
|
|
||||||
|
|
||||||
private val terminal = TerminalBuilder.builder()
|
private val terminal = TerminalBuilder.builder()
|
||||||
.system(true)
|
.system(true)
|
||||||
@@ -26,7 +23,7 @@ class IOManager {
|
|||||||
private val lineReader: LineReader = LineReaderBuilder.builder()
|
private val lineReader: LineReader = LineReaderBuilder.builder()
|
||||||
.terminal(terminal)
|
.terminal(terminal)
|
||||||
.history(history)
|
.history(history)
|
||||||
.completer(StringsCompleter(commandValidator.getKnownCommands()))
|
// .completer(StringsCompleter(invoker.getCommandNames()))
|
||||||
.option(LineReader.Option.HISTORY_IGNORE_DUPS, true)
|
.option(LineReader.Option.HISTORY_IGNORE_DUPS, true)
|
||||||
.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
|
.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
import lab6.prog.network.AppJson
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.PortUnreachableException
|
import java.net.PortUnreachableException
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import commands.ClientCommand
|
||||||
|
import commands.CommandType
|
||||||
|
import input.HumanBeingBuilder
|
||||||
|
import input.IOManager
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
class CommandInvoker(private val io: IOManager) {
|
||||||
|
private val commands = mutableMapOf<String, ClientCommand>()
|
||||||
|
private val history = ArrayDeque<String>(12)
|
||||||
|
|
||||||
|
fun register(command: ClientCommand) {
|
||||||
|
commands[command.name] = command
|
||||||
|
}
|
||||||
|
|
||||||
|
fun execute(input: String) {
|
||||||
|
val parts = input.trim().split(" ")
|
||||||
|
val name = parts[0].lowercase().trim()
|
||||||
|
val args = parts.drop(1)
|
||||||
|
|
||||||
|
val command = commands[name]
|
||||||
|
if (command == null) {
|
||||||
|
logger.info { "Неизвестная команда: '$name'. Введите 'help' для справки."}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addToHistory(name)
|
||||||
|
try {
|
||||||
|
if (command.type == CommandType.MODEL) {
|
||||||
|
val model = HumanBeingBuilder(io).build()
|
||||||
|
val res = command.execute(args, model)
|
||||||
|
io.print(res)
|
||||||
|
} else {
|
||||||
|
val res = command.execute(args)
|
||||||
|
io.print(res)
|
||||||
|
}
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
logger.info {"[Ошибка выполнения] ${e.message}"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHistory(): List<String> = history.toList()
|
||||||
|
|
||||||
|
fun getCommands(): Map<String, ClientCommand> = commands.toMap()
|
||||||
|
|
||||||
|
private fun addToHistory(name: String) {
|
||||||
|
if (history.size >= 12) history.removeFirst()
|
||||||
|
history.addLast(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCommandNames(): List<String> = commands.keys.toList()
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package runner
|
|
||||||
|
|
||||||
class CommandValidator {
|
|
||||||
|
|
||||||
// Команды, которые требуют ввода HumanBeing
|
|
||||||
private val humanBeingCommands = setOf("add", "add_if_max", "add_if_min")
|
|
||||||
|
|
||||||
// Команды с аргументом
|
|
||||||
private val commandsWArgs = setOf("update", "remove_by_id", "execute_script")
|
|
||||||
|
|
||||||
// Все команды, доступные клиенту
|
|
||||||
private val knownCommands = setOf(
|
|
||||||
"help", "info", "show", "add", "update",
|
|
||||||
"remove_by_id", "clear", "execute_script",
|
|
||||||
"add_if_max", "add_if_min", "history",
|
|
||||||
"sum_of_minutes_of_waiting", "min_by_name",
|
|
||||||
"print_field_descending_minutes_of_waiting",
|
|
||||||
"exit"
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getKnownCommands(): Set<String> {return knownCommands}
|
|
||||||
|
|
||||||
fun validate(command: String, args: List<String>?): Pair<Boolean, String?> {
|
|
||||||
if (command !in knownCommands){
|
|
||||||
return Pair(false, "Неизвестная команда: '$command'. Введите 'help' для справки.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command in commandsWArgs && args?.isEmpty() == true) {
|
|
||||||
return if (command == "execute_script") {
|
|
||||||
Pair(false, "[Ошибка] Укажите путь к файлу. Пример: execute_script script.txt")
|
|
||||||
} else {
|
|
||||||
// maybe create another set w commands w file args and another for other args
|
|
||||||
Pair(false, "[Ошибка] Укажите id элемента. Пример: $command <uuid>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Pair(true, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isBuildsHumanBeing(command: String): Boolean {
|
|
||||||
return command in humanBeingCommands
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
class ModelValidator {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
|
interface Command {
|
||||||
|
val name: String
|
||||||
|
val description: String
|
||||||
|
val args: List<CommandArgType>
|
||||||
|
val type: CommandType
|
||||||
|
|
||||||
|
fun execute(args: List<String>, humanBeing: HumanBeing? = null): Response
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
// why there's only two types? yes.
|
||||||
|
@Serializable
|
||||||
|
enum class CommandArgType {
|
||||||
|
STRING,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
enum class CommandType {
|
||||||
|
SIMPLE,
|
||||||
|
MODEL
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import lab6.prog.network.utils.LocalDateSerializer
|
import network.utils.LocalDateSerializer
|
||||||
import lab6.prog.network.utils.UUIDSerializer
|
import network.utils.UUIDSerializer
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lab6.prog.network
|
package network
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import commands.CommandArgType
|
||||||
|
import commands.CommandType
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CommandInfo(
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val args: List<CommandArgType>,
|
||||||
|
val type: CommandType
|
||||||
|
)
|
||||||
@@ -7,9 +7,15 @@ import kotlinx.serialization.Serializable
|
|||||||
*
|
*
|
||||||
* @property success true — команда выполнена, false — ошибка
|
* @property success true — команда выполнена, false — ошибка
|
||||||
* @property message текст для вывода пользователю
|
* @property message текст для вывода пользователю
|
||||||
|
*
|
||||||
|
* -- только при синхронизации --
|
||||||
|
* @property knownCommands - общий список известных команд
|
||||||
|
* @property humanBeingCommands - команды, требующие ввода коллекции [models.HumanBeing]
|
||||||
|
* @property commandsWArgs - команды, требующие ввода аргументов
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Response(
|
data class Response(
|
||||||
val success: Boolean,
|
val success: Boolean,
|
||||||
val message: String
|
val message: String,
|
||||||
|
val commands: List<CommandInfo>? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lab6.prog.network.utils
|
package network.utils
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.descriptors.*
|
import kotlinx.serialization.descriptors.*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package lab6.prog.network.utils
|
package network.utils
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.descriptors.*
|
import kotlinx.serialization.descriptors.*
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ dependencies {
|
|||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
||||||
implementation 'io.github.microutils:kotlin-logging-jvm:3.0.5'
|
implementation 'io.github.microutils:kotlin-logging-jvm:3.0.5'
|
||||||
implementation "io.ktor:ktor-network:3.4.2"
|
implementation "io.ktor:ktor-network:3.4.2"
|
||||||
|
implementation "io.github.oshai:kotlin-logging-jvm:7.0.3"
|
||||||
|
implementation "org.slf4j:slf4j-api:2.0.13"
|
||||||
|
implementation "ch.qos.logback:logback-classic:1.5.6"
|
||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
import commands.*
|
import commands.*
|
||||||
import file.FileManager
|
import file.FileManager
|
||||||
|
import mu.KotlinLogging
|
||||||
import network.NetworkManager
|
import network.NetworkManager
|
||||||
import runner.CommandInvoker
|
import runner.CommandInvoker
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
System.setProperty("slf4j.internal.verbosity", "WARN")
|
System.setProperty("slf4j.internal.verbosity", "WARN")
|
||||||
|
|
||||||
val filePath = if (args.isNotEmpty()) args[0] else "data.csv"
|
val filePath = if (args.isNotEmpty()) args[0] else "data.csv"
|
||||||
|
val port = if (args.isNotEmpty() && args.size > 1) args[1].toInt() else 8080
|
||||||
|
|
||||||
|
logger.info{ "[startup] Starting server on port $port with $args args" }
|
||||||
|
|
||||||
val fileManager = FileManager(filePath)
|
val fileManager = FileManager(filePath)
|
||||||
val manager = CollectionManager()
|
val manager = CollectionManager()
|
||||||
@@ -21,9 +27,9 @@ fun main(args: Array<String>) {
|
|||||||
registerServerCommands(invoker, manager, fileManager)
|
registerServerCommands(invoker, manager, fileManager)
|
||||||
|
|
||||||
// Сохранение при завершении (Ctrl+C или kill)
|
// Сохранение при завершении (Ctrl+C или kill)
|
||||||
val server = NetworkManager(port = 8080, invoker = invoker)
|
val server = NetworkManager(port = port, invoker = invoker)
|
||||||
Runtime.getRuntime().addShutdownHook(Thread {
|
Runtime.getRuntime().addShutdownHook(Thread {
|
||||||
println("[Server] Сохранение коллекции перед выходом...")
|
logger.info{ "Saving collection before exit..." }
|
||||||
fileManager.write(manager.getAll())
|
fileManager.write(manager.getAll())
|
||||||
server.stop()
|
server.stop()
|
||||||
})
|
})
|
||||||
@@ -31,18 +37,18 @@ fun main(args: Array<String>) {
|
|||||||
// Консоль на сервере
|
// Консоль на сервере
|
||||||
Thread {
|
Thread {
|
||||||
val scanner = java.util.Scanner(System.`in`)
|
val scanner = java.util.Scanner(System.`in`)
|
||||||
println("[Server] Серверные команды: save, exit")
|
logger.info{ "[Server-console] Server-side commands: save, exit" }
|
||||||
while (scanner.hasNextLine()) {
|
while (scanner.hasNextLine()) {
|
||||||
when (scanner.nextLine().trim().lowercase()) {
|
when (scanner.nextLine().trim().lowercase()) {
|
||||||
"save" -> {
|
"save" -> {
|
||||||
fileManager.write(manager.getAll())
|
fileManager.write(manager.getAll())
|
||||||
println("[Server] Коллекция сохранена.")
|
logger.info{ "[Server-console] Collection is saved" }
|
||||||
}
|
}
|
||||||
"exit" -> {
|
"exit" -> {
|
||||||
println("[Server] Завершение работы...")
|
logger.info{ "[Server-console] Shutting down..." }
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
}
|
}
|
||||||
else -> println("[Server] Доступные команды: save, exit")
|
else -> logger.info{ "[Server-console] Server-side commands: save, exit" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also { it.isDaemon = true }.start()
|
}.also { it.isDaemon = true }.start()
|
||||||
@@ -56,7 +62,7 @@ fun registerServerCommands(
|
|||||||
fileManager: FileManager
|
fileManager: FileManager
|
||||||
) {
|
) {
|
||||||
listOf(
|
listOf(
|
||||||
HelpCommand(invoker),
|
// HelpCommand(invoker),
|
||||||
InfoCommand(manager),
|
InfoCommand(manager),
|
||||||
ShowCommand(manager),
|
ShowCommand(manager),
|
||||||
AddCommand(manager),
|
AddCommand(manager),
|
||||||
@@ -67,10 +73,11 @@ fun registerServerCommands(
|
|||||||
AddIfMaxCommand(manager),
|
AddIfMaxCommand(manager),
|
||||||
AddIfMinCommand(manager),
|
AddIfMinCommand(manager),
|
||||||
HistoryCommand(invoker),
|
HistoryCommand(invoker),
|
||||||
GetCommandsCommand(invoker),
|
SyncCommand(invoker),
|
||||||
SumOfMinutesCommand(manager),
|
SumOfMinutesCommand(manager),
|
||||||
MinByNameCommand(manager),
|
MinByNameCommand(manager),
|
||||||
PrintDescendingMinutesCommand(manager),
|
PrintDescendingMinutesCommand(manager),
|
||||||
ExitCommand()
|
ExitCommand()
|
||||||
).forEach { invoker.register(it) }
|
).forEach { invoker.register(it)}
|
||||||
|
logger.info{ " [startup] Registered commands: ${invoker.getCommands().keys}" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import java.util.UUID
|
|||||||
|
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
|
||||||
class CollectionManager {
|
class CollectionManager {
|
||||||
|
|
||||||
private val collection: TreeSet<HumanBeing> = TreeSet()
|
private val collection: TreeSet<HumanBeing> = TreeSet()
|
||||||
val initDate: LocalDate = LocalDate.now()
|
val initDate: LocalDate = LocalDate.now()
|
||||||
|
|
||||||
fun add(humanBeing: HumanBeing): Boolean {
|
fun add(humanBeing: HumanBeing): Boolean {
|
||||||
logger.info { "[ADD] Создан элемент ${humanBeing.id}" }
|
logger.info { "[ADD] Element created ${humanBeing.id}" }
|
||||||
return collection.add(humanBeing)
|
return collection.add(humanBeing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,19 +23,19 @@ class CollectionManager {
|
|||||||
val old = getById(id) ?: return false
|
val old = getById(id) ?: return false
|
||||||
collection.remove(old)
|
collection.remove(old)
|
||||||
val replaced = updated.copy(id = old.id, creationDate = old.creationDate)
|
val replaced = updated.copy(id = old.id, creationDate = old.creationDate)
|
||||||
logger.info { "[UPDATE] Обновлён элемент ${old.id}" }
|
logger.info { "[UPDATE] Element updated ${old.id}" }
|
||||||
return collection.add(replaced)
|
return collection.add(replaced)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeById(id: UUID): Boolean {
|
fun removeById(id: UUID): Boolean {
|
||||||
val target = getById(id) ?: return false
|
val target = getById(id) ?: return false
|
||||||
logger.info { "[REMOVE] Удалён элемент ${target.id}" }
|
logger.info { "[REMOVE] Element deleted ${target.id}" }
|
||||||
return collection.remove(target)
|
return collection.remove(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
collection.clear()
|
collection.clear()
|
||||||
logger.info { "[CLEAR] Коллекция очищена" }
|
logger.info { "[CLEAR] Collection cleared" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): TreeSet<HumanBeing> = TreeSet(collection)
|
fun getAll(): TreeSet<HumanBeing> = TreeSet(collection)
|
||||||
@@ -78,7 +79,7 @@ class CollectionManager {
|
|||||||
fun loadFromFile(items: List<HumanBeing>) {
|
fun loadFromFile(items: List<HumanBeing>) {
|
||||||
collection.clear()
|
collection.clear()
|
||||||
items.stream().forEach { collection.add(it) }
|
items.stream().forEach { collection.add(it) }
|
||||||
logger.info { "[LOAD] Загружено ${items.size} элементов" }
|
logger.info { "[LOAD] loaded ${items.size} elements" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getInfo(): String =
|
fun getInfo(): String =
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class AddCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class AddCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "add"
|
override val name = "add"
|
||||||
override val description = "добавить новый элемент в коллекцию"
|
override val description = "добавить новый элемент в коллекцию"
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
override val type = CommandType.MODEL
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
humanBeing ?: return "[Ошибка] Объект HumanBeing не передан."
|
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||||
return if (manager.add(humanBeing))
|
return if (manager.add(humanBeing))
|
||||||
"Элемент добавлен: ${humanBeing.name} [${humanBeing.id}]"
|
Response(true, "Элемент добавлен: ${humanBeing.name} [${humanBeing.id}]")
|
||||||
else
|
else
|
||||||
"Элемент уже существует в коллекции."
|
Response(false, "Элемент уже существует в коллекции.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ package commands
|
|||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class AddIfMaxCommand(private val manager: CollectionManager) : Command {
|
class AddIfMaxCommand(private val manager: CollectionManager) : Command {
|
||||||
override val name = "add_if_max"
|
override val name = "add_if_max"
|
||||||
override val description = "добавить элемент, если он больше максимального"
|
override val description = "добавить элемент, если он больше максимального"
|
||||||
|
override val type = CommandType.MODEL
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
humanBeing ?: return "[Ошибка] Объект HumanBeing не передан."
|
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||||
val max = manager.getMax()
|
val max = manager.getMax()
|
||||||
return if (max == null || humanBeing > max) {
|
return if (max == null || humanBeing > max) {
|
||||||
manager.add(humanBeing)
|
manager.add(humanBeing)
|
||||||
"Элемент добавлен (превышает максимум)."
|
Response(true, "Элемент добавлен (превышает максимум).")
|
||||||
} else {
|
} else {
|
||||||
"Элемент не добавлен: не превышает текущий максимум (${max.name})."
|
Response(false, "Элемент не добавлен: не превышает текущий максимум (${max.name}).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,25 @@ package commands
|
|||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class AddIfMinCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class AddIfMinCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "add_if_min"
|
override val name = "add_if_min"
|
||||||
override val description = "добавить элемент, если он меньше минимального"
|
override val description = "добавить элемент, если он меньше минимального"
|
||||||
|
override val type = CommandType.MODEL
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
humanBeing ?: return "[Ошибка] Объект HumanBeing не передан."
|
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||||
val min = manager.getMin()
|
val min = manager.getMin()
|
||||||
return if (min == null || humanBeing < min) {
|
return if (min == null || humanBeing < min) {
|
||||||
manager.add(humanBeing)
|
manager.add(humanBeing)
|
||||||
"Элемент добавлен (меньше минимума)."
|
Response(true, "Элемент добавлен (меньше минимума).")
|
||||||
} else {
|
} else {
|
||||||
"Элемент не добавлен: не меньше текущего минимума (${min.name})."
|
Response(false, "Элемент не добавлен: не меньше текущего минимума (${min.name}).")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class ClearCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class ClearCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "clear"
|
override val name = "clear"
|
||||||
override val description = "очистить коллекцию"
|
override val description = "очистить коллекцию"
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
manager.clear()
|
manager.clear()
|
||||||
return "Коллекция очищена."
|
return Response(true, "Коллекция очищена.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Базовый интерфейс команды.
|
* Базовый интерфейс команды.
|
||||||
@@ -11,11 +12,13 @@ import models.HumanBeing
|
|||||||
interface Command {
|
interface Command {
|
||||||
val name: String
|
val name: String
|
||||||
val description: String
|
val description: String
|
||||||
|
val type : CommandType
|
||||||
|
val args : List<CommandArgType>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param args строковые аргументы (UUID и т.п.)
|
* @param args строковые аргументы (UUID и т.п.)
|
||||||
* @param humanBeing объект из запроса — только для add/update/add_if_max/add_if_min
|
* @param humanBeing объект из запроса — только для add/update/add_if_max/add_if_min
|
||||||
* @return строка-результат для клиента
|
* @return строка-результат для клиента
|
||||||
*/
|
*/
|
||||||
fun execute(args: List<String>, humanBeing: HumanBeing? = null): String
|
fun execute(args: List<String>, humanBeing: HumanBeing? = null): Response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ package commands
|
|||||||
/**
|
/**
|
||||||
* Завершает программу без сохранения.
|
* Завершает программу без сохранения.
|
||||||
*/
|
*/
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
|
@Serializable
|
||||||
class ExitCommand() : Command {
|
class ExitCommand() : Command {
|
||||||
override val name = "exit"
|
override val name = "exit"
|
||||||
override val description = "завершить программу"
|
override val description = "завершить программу"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String = ""
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response = Response(true, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import models.HumanBeing
|
|
||||||
import runner.CommandInvoker
|
|
||||||
|
|
||||||
class GetCommandsCommand (
|
|
||||||
private val invoker: CommandInvoker
|
|
||||||
): Command {
|
|
||||||
override val name: String = "GetCommands"
|
|
||||||
override val description: String = "Получает все доступные с сервера команды"
|
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
|
||||||
return invoker.getCommands().toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import runner.CommandInvoker
|
import runner.CommandInvoker
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class HistoryCommand(private val invoker: CommandInvoker) : Command {
|
@Serializable
|
||||||
|
class HistoryCommand(@Contextual private val invoker: CommandInvoker) : Command {
|
||||||
override val name = "history"
|
override val name = "history"
|
||||||
override val description = "вывести последние 12 команд"
|
override val description = "вывести последние 12 команд"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
val history = invoker.getHistory()
|
val history = invoker.getHistory()
|
||||||
if (history.isEmpty()) return "История пуста."
|
if (history.isEmpty()) return Response(true, "История пуста.")
|
||||||
return history.mapIndexed { i, cmd -> " ${i + 1}. $cmd" }.joinToString("\n")
|
return Response(true, history.mapIndexed { i, cmd -> " ${i + 1}. $cmd" }.joinToString("\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class InfoCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class InfoCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "info"
|
override val name = "info"
|
||||||
override val description = "вывести информацию о коллекции"
|
override val description = "вывести информацию о коллекции"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||||
manager.getInfo()
|
Response(true, manager.getInfo())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выводит любой элемент с минимальным значением поля [HumanBeing.name].
|
* Выводит любой элемент с минимальным значением поля [HumanBeing.name].
|
||||||
*/
|
*/
|
||||||
class MinByNameCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class MinByNameCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "min_by_name"
|
override val name = "min_by_name"
|
||||||
override val description = "вывести элемент с минимальным именем"
|
override val description = "вывести элемент с минимальным именем"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||||
manager.minByName()?.toString() ?: "Коллекция пуста."
|
Response(true, manager.minByName()?.toString() ?: "Коллекция пуста.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выводит значения поля [models.HumanBeing.minutesOfWaiting] в порядке убывания.
|
* Выводит значения поля [models.HumanBeing.minutesOfWaiting] в порядке убывания.
|
||||||
*/
|
*/
|
||||||
class PrintDescendingMinutesCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class PrintDescendingMinutesCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "print_field_descending_minutes_of_waiting"
|
override val name = "print_field_descending_minutes_of_waiting"
|
||||||
override val description = "вывести minutesOfWaiting в порядке убывания"
|
override val description = "вывести minutesOfWaiting в порядке убывания"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
val values = manager.getMinutesOfWaitingDescending()
|
val values = manager.getMinutesOfWaitingDescending()
|
||||||
return if (values.isEmpty()) "Коллекция пуста."
|
return if (values.isEmpty()) Response(true, "Коллекция пуста.")
|
||||||
else values.joinToString("\n")
|
else Response(true, values.joinToString("\n"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class RemoveByIdCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class RemoveByIdCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "remove_by_id"
|
override val name = "remove_by_id"
|
||||||
override val description = "удалить элемент по id: remove_by_id <uuid>"
|
override val description = "удалить элемент по id: remove_by_id <uuid>"
|
||||||
|
override val type = CommandType.MODEL
|
||||||
|
override val args = listOf(CommandArgType.STRING)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
if (args.isEmpty()) return "[Ошибка] Укажите id. Пример: remove_by_id <uuid>"
|
if (args.isEmpty()) return Response(false, "[Ошибка] Укажите id. Пример: remove_by_id <uuid>")
|
||||||
|
|
||||||
val id = try {
|
val id = try {
|
||||||
UUID.fromString(args[0])
|
UUID.fromString(args[0])
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
return "[Ошибка] Некорректный UUID: '${args[0]}'"
|
return Response(false, "[Ошибка] Некорректный UUID: '${args[0]}'")
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (manager.removeById(id)) "Элемент удалён."
|
return if (manager.removeById(id)) Response(true, "Элемент удалён.")
|
||||||
else "[Ошибка] Элемент с id '$id' не найден."
|
else Response(false, "[Ошибка] Элемент с id '$id' не найден.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,23 @@ package commands
|
|||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
import file.FileManager
|
import file.FileManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
|
@Serializable
|
||||||
class SaveCommand(
|
class SaveCommand(
|
||||||
private val manager: CollectionManager,
|
@Contextual private val manager: CollectionManager,
|
||||||
private val fileManager: FileManager
|
@Contextual private val fileManager: FileManager
|
||||||
) : Command {
|
) : Command {
|
||||||
override val name = "save"
|
override val name = "save"
|
||||||
override val description = "[SERVER] сохранить коллекцию в файл"
|
override val description = "[SERVER] сохранить коллекцию в файл"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
return if (fileManager.write(manager.getAll())) "Коллекция сохранена."
|
return if (fileManager.write(manager.getAll())) Response(true, "Коллекция сохранена.")
|
||||||
else "[Ошибка] Не удалось сохранить коллекцию."
|
else Response(false, "[Ошибка] Не удалось сохранить коллекцию.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
class ShowCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class ShowCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "show"
|
override val name = "show"
|
||||||
override val description = "вывести все элементы коллекции"
|
override val description = "вывести все элементы коллекции"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
if (manager.isEmpty()) return "Коллекция пуста."
|
if (manager.isEmpty()) return Response(true, "Коллекция пуста.")
|
||||||
return manager.getAll().joinToString("\n") { it.toString() }
|
return Response(true, manager.getAll().joinToString("\n") { it.toString() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выводит сумму значений поля [models.HumanBeing.minutesOfWaiting].
|
* Выводит сумму значений поля [models.HumanBeing.minutesOfWaiting].
|
||||||
*/
|
*/
|
||||||
class SumOfMinutesCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class SumOfMinutesCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "sum_of_minutes_of_waiting"
|
override val name = "sum_of_minutes_of_waiting"
|
||||||
override val description = "вывести сумму minutesOfWaiting всех элементов"
|
override val description = "вывести сумму minutesOfWaiting всех элементов"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response =
|
||||||
"Сумма minutesOfWaiting: ${manager.sumOfMinutesOfWaiting()}"
|
Response(true, "Сумма minutesOfWaiting: ${manager.sumOfMinutesOfWaiting()}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import models.HumanBeing
|
||||||
|
import network.CommandInfo
|
||||||
|
import network.Response
|
||||||
|
import runner.CommandInvoker
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class SyncCommand(
|
||||||
|
@Contextual private val invoker: CommandInvoker
|
||||||
|
) : Command {
|
||||||
|
override val name: String = "sync"
|
||||||
|
override val description: String = "Получает все доступные с сервера команды"
|
||||||
|
override val type = CommandType.SIMPLE
|
||||||
|
override val args = listOf(CommandArgType.NONE)
|
||||||
|
|
||||||
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
|
|
||||||
|
val commands = mutableMapOf<String, Command>()
|
||||||
|
|
||||||
|
for (command in invoker.getCommands().values) {
|
||||||
|
if (command.name == "sync" || command.name == "exit") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
commands[command.name] = command
|
||||||
|
}
|
||||||
|
|
||||||
|
val commandInfos = commands.values.map { command ->
|
||||||
|
CommandInfo(
|
||||||
|
name = command.name,
|
||||||
|
description = command.description,
|
||||||
|
type = command.type,
|
||||||
|
args = command.args
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
success = true,
|
||||||
|
message = "хуй",
|
||||||
|
commands = commandInfos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import collection.CollectionManager
|
import collection.CollectionManager
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
|
import network.Response
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class UpdateCommand(private val manager: CollectionManager) : Command {
|
@Serializable
|
||||||
|
class UpdateCommand(@Contextual private val manager: CollectionManager) : Command {
|
||||||
override val name = "update"
|
override val name = "update"
|
||||||
override val description = "обновить элемент по id: update <uuid>"
|
override val description = "обновить элемент по id: update <uuid>"
|
||||||
|
override val type = CommandType.MODEL
|
||||||
|
override val args = listOf(CommandArgType.STRING)
|
||||||
|
|
||||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
override fun execute(args: List<String>, humanBeing: HumanBeing?): Response {
|
||||||
if (args.isEmpty()) return "[Ошибка] Укажите id. Пример: update <uuid>"
|
if (args.isEmpty()) return Response(false, "[Ошибка] Укажите id. Пример: update <uuid>")
|
||||||
humanBeing ?: return "[Ошибка] Объект HumanBeing не передан."
|
humanBeing ?: return Response(false, "[Ошибка] Объект HumanBeing не передан.")
|
||||||
|
|
||||||
val id = try {
|
val id = try {
|
||||||
UUID.fromString(args[0])
|
UUID.fromString(args[0])
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
return "[Ошибка] Некорректный UUID: '${args[0]}'"
|
return Response(false, "[Ошибка] Некорректный UUID: '${args[0]}'")
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (manager.update(id, humanBeing)) "Элемент обновлён."
|
return if (manager.update(id, humanBeing)) Response(true, "Элемент обновлён.")
|
||||||
else "[Ошибка] Элемент с id '$id' не найден."
|
else Response(false, "[Ошибка] Элемент с id '$id' не найден.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import models.Car
|
|||||||
import models.Coordinates
|
import models.Coordinates
|
||||||
import models.HumanBeing
|
import models.HumanBeing
|
||||||
import models.WeaponType
|
import models.WeaponType
|
||||||
|
import mu.KotlinLogging
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
@@ -12,6 +13,8 @@ import java.time.format.DateTimeParseException
|
|||||||
import java.util.Scanner
|
import java.util.Scanner
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Менеджер файлового хранилища коллекции.
|
* Менеджер файлового хранилища коллекции.
|
||||||
*
|
*
|
||||||
@@ -45,12 +48,12 @@ class FileManager(private val filePath: String) {
|
|||||||
val file = File(filePath)
|
val file = File(filePath)
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
System.err.println("Файл '$filePath' не найден. Коллекция будет пустой.")
|
logger.warn{"There's no '$filePath'. Collection will be empty."}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.canRead()) {
|
if (!file.canRead()) {
|
||||||
System.err.println("Нет прав на чтение файла '$filePath'. Коллекция будет пустой.")
|
logger.warn{"Permission denied: '$filePath'. Collection will be empty."}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +66,23 @@ class FileManager(private val filePath: String) {
|
|||||||
val line = scanner.nextLine().trim()
|
val line = scanner.nextLine().trim()
|
||||||
lineNumber++
|
lineNumber++
|
||||||
|
|
||||||
// Пропускаем заголовок и пустые строки
|
// skip header and empty lines
|
||||||
if (lineNumber == 1 && line.startsWith("id")) continue
|
if (lineNumber == 1 && line.startsWith("id")) continue
|
||||||
if (line.isBlank()) continue
|
if (line.isBlank()) continue
|
||||||
|
|
||||||
parseLine(line, lineNumber)
|
val parsedLine = parseLine(line, lineNumber)
|
||||||
?.let { result.add(it) }
|
if (parsedLine != null) {
|
||||||
?: System.err.println("Строка $lineNumber пропущена: некорректные данные → '$line'")
|
result.add(parsedLine)
|
||||||
|
} else {
|
||||||
|
logger.warn { "Line $lineNumber of $filePath is skipped: invalid data → '$line'" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
System.err.println("Файл не найден во время чтения: ${e.message}")
|
logger.warn(e) {"File not found: ${e.message}"}
|
||||||
}
|
}
|
||||||
|
|
||||||
println("Загружено элементов: ${result.size} из файла '$filePath'")
|
logger.info {"${result.size} elements loaded from'$filePath'"}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +97,7 @@ class FileManager(private val filePath: String) {
|
|||||||
return try {
|
return try {
|
||||||
val parts = line.split(DELIMITER, limit = 12)
|
val parts = line.split(DELIMITER, limit = 12)
|
||||||
if (parts.size < 12) {
|
if (parts.size < 12) {
|
||||||
System.err.println("Строка $lineNumber: ожидалось 12 полей, найдено ${parts.size}")
|
logger.warn {"Line $lineNumber: expected 12 fields, found ${parts.size}"}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,24 +117,16 @@ class FileManager(private val filePath: String) {
|
|||||||
}
|
}
|
||||||
val carCool = parts[11].trim().toBoolean()
|
val carCool = parts[11].trim().toBoolean()
|
||||||
|
|
||||||
// самая лучшая валидация (100%)
|
|
||||||
require(name.isNotBlank()) { "name не может быть пустым" }
|
require(name.isNotBlank()) { "name не может быть пустым" }
|
||||||
|
|
||||||
// от такого может быть диабет
|
|
||||||
HumanBeing(
|
HumanBeing(
|
||||||
id = id, name = name, coordinates = Coordinates(coordX, coordY),
|
id = id, name = name, coordinates = Coordinates(coordX, coordY),
|
||||||
creationDate = creationDate, realHero = realHero, hasToothpick = hasToothpick,
|
creationDate = creationDate, realHero = realHero, hasToothpick = hasToothpick,
|
||||||
impactSpeed = impactSpeed, soundtrackName = soundtrackName,
|
impactSpeed = impactSpeed, soundtrackName = soundtrackName,
|
||||||
minutesOfWaiting = minutesOfWaiting, weaponType = weaponType, car = Car(carCool)
|
minutesOfWaiting = minutesOfWaiting, weaponType = weaponType, car = Car(carCool)
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
System.err.println("Строка $lineNumber — ошибка данных: ${e.message}")
|
|
||||||
null
|
|
||||||
} catch (e: DateTimeParseException) {
|
|
||||||
System.err.println("Строка $lineNumber — неверный формат даты: ${e.message}")
|
|
||||||
null
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
System.err.println("Строка $lineNumber — неожиданная ошибка: ${e.message}")
|
logger.warn(e) {"Line $lineNumber: ${e.message}"}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +141,7 @@ class FileManager(private val filePath: String) {
|
|||||||
val file = File(filePath)
|
val file = File(filePath)
|
||||||
|
|
||||||
if (file.exists() && !file.canWrite()) {
|
if (file.exists() && !file.canWrite()) {
|
||||||
System.err.println("Нет прав на запись в файл '$filePath'.")
|
logger.warn{"Permission denied: '$filePath'."}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,13 +154,13 @@ class FileManager(private val filePath: String) {
|
|||||||
writer.write("\n")
|
writer.write("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("Коллекция сохранена в '$filePath' (${items.size} элементов).")
|
logger.info {"Collection saved to'$filePath' (${items.size} elements)."}
|
||||||
true
|
true
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
println("Отказано в доступе к файлу '$filePath': ${e.message}")
|
logger.warn(e) {"Permission denied: '$filePath'."}
|
||||||
false
|
false
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Ошибка записи в файл: ${e.message}")
|
logger.warn(e) {"An error occurred: '$filePath'."}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
import lab6.prog.network.AppJson
|
import mu.KotlinLogging
|
||||||
import runner.CommandInvoker
|
import runner.CommandInvoker
|
||||||
import java.net.DatagramPacket
|
import java.net.DatagramPacket
|
||||||
import java.net.DatagramSocket
|
import java.net.DatagramSocket
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
|
|
||||||
private const val BUFFER_SIZE = 65507
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UDP-сервер на основе DatagramSocket (датаграммы).
|
* UDP-сервер на основе DatagramSocket (датаграммы).
|
||||||
@@ -24,37 +24,30 @@ class NetworkManager(
|
|||||||
private val socket = DatagramSocket(port)
|
private val socket = DatagramSocket(port)
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
println("[Server] Запущен на порту $port. Ожидание клиентов...")
|
logger.info{"Server started on port $port. Waiting for connection..."}
|
||||||
val buf = ByteArray(BUFFER_SIZE)
|
val buf = ByteArray(65507)
|
||||||
|
|
||||||
while (!socket.isClosed) {
|
while (!socket.isClosed) {
|
||||||
try {
|
try {
|
||||||
// 1. Принять датаграмму
|
|
||||||
val packet = DatagramPacket(buf, buf.size)
|
val packet = DatagramPacket(buf, buf.size)
|
||||||
socket.receive(packet)
|
socket.receive(packet)
|
||||||
|
|
||||||
val json = String(packet.data, 0, packet.length, Charsets.UTF_8)
|
val json = String(packet.data, 0, packet.length, Charsets.UTF_8)
|
||||||
|
|
||||||
// 2. Десериализовать запрос
|
|
||||||
val request = try {
|
val request = try {
|
||||||
AppJson.decodeFromString<Request>(json)
|
AppJson.decodeFromString<Request>(json)
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
System.err.println("[Server] Некорректный JSON: ${e.message}")
|
logger.warn(e) { "Invalid request: ${e.message}" }
|
||||||
sendResponse(Response(false, "Некорректный формат запроса"), packet.address, packet.port)
|
sendResponse(Response(false, "Некорректный формат запроса"), packet.address, packet.port)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
logger.info{"'${request.commandName}' from ${packet.address}:${packet.port}."}
|
||||||
|
|
||||||
println("[Server] '${request.commandName}' от ${packet.address}:${packet.port}")
|
|
||||||
|
|
||||||
// 3. Выполнить и получить ответ
|
|
||||||
val response = invoker.execute(request)
|
val response = invoker.execute(request)
|
||||||
|
|
||||||
// 4. Отправить ответ
|
|
||||||
sendResponse(response, packet.address, packet.port)
|
sendResponse(response, packet.address, packet.port)
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (!socket.isClosed)
|
if (!socket.isClosed)
|
||||||
System.err.println("[Server] Ошибка: ${e.message}")
|
logger.warn(e) { "Error: ${e.message}" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +58,7 @@ class NetworkManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stop() {
|
fun stop() {
|
||||||
println("[Server] Остановка...")
|
logger.info{ "Shutting down..." }
|
||||||
socket.close()
|
socket.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import network.Response
|
|||||||
*/
|
*/
|
||||||
class CommandInvoker {
|
class CommandInvoker {
|
||||||
|
|
||||||
private val serverOnlyCommands = setOf("save")
|
private val serverOnlyCommands = setOf("save", "sync-server")
|
||||||
private val commands = mutableMapOf<String, Command>()
|
private val commands = mutableMapOf<String, Command>()
|
||||||
private val history = ArrayDeque<String>()
|
private val history = ArrayDeque<String>()
|
||||||
|
|
||||||
fun register(command: Command) {
|
fun register(command: Command) {
|
||||||
commands[command.name] = command
|
commands[command.name] = command
|
||||||
@@ -31,8 +31,7 @@ class CommandInvoker {
|
|||||||
addToHistory(request.commandName)
|
addToHistory(request.commandName)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val result = command.execute(request.args, request.humanBeing)
|
command.execute(request.args, request.humanBeing)
|
||||||
Response(true, result)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Response(false, "[Ошибка выполнения] ${e.message}")
|
Response(false, "[Ошибка выполнения] ${e.message}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,20 @@
|
|||||||
<configuration debug="false" statusListenerClass="ch.qos.logback.core.status.NopStatusListener">
|
<configuration>
|
||||||
|
<appender name="server_log" class="ch.qos.logback.core.FileAppender">
|
||||||
<appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<file>logs/server.log</file>
|
||||||
<file>logs/all.log</file>
|
|
||||||
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
|
||||||
<fileNamePattern>logs/archived/all-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
|
|
||||||
|
|
||||||
<maxFileSize>5MB</maxFileSize>
|
|
||||||
|
|
||||||
<maxHistory>30</maxHistory>
|
|
||||||
|
|
||||||
<totalSizeCap>100MB</totalSizeCap>
|
|
||||||
</rollingPolicy>
|
|
||||||
|
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<file>logs/errors.log</file>
|
|
||||||
|
|
||||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
|
||||||
<level>WARN</level>
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
|
||||||
<fileNamePattern>logs/archived/errors-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
|
||||||
<maxFileSize>2MB</maxFileSize>
|
|
||||||
<maxHistory>10</maxHistory>
|
|
||||||
</rollingPolicy>
|
|
||||||
|
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="DEBUG">
|
<root level="TRACE">
|
||||||
<appender-ref ref="ALL_FILE" />
|
<appender-ref ref="server_log" />
|
||||||
<appender-ref ref="ERROR_FILE" />
|
<appender-ref ref="console" />
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
Reference in New Issue
Block a user