diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 09f8071..ec2dde6 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -23,7 +23,9 @@ "Bash(./gradlew compileJava -q 2>&1 | tail -30)", "Bash(ls -lah /home/mario/Workspaces/xxx-thegame/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/*.java)", "Bash(for f:*)", - "Bash(ls:*)" + "Bash(ls:*)", + "Bash(./gradlew compileJava)", + "Bash(./gradlew build:*)" ] } } diff --git a/.metadata/.lock_info b/.metadata/.lock_info index c1de82b..f5a0191 100644 --- a/.metadata/.lock_info +++ b/.metadata/.lock_info @@ -1,5 +1,5 @@ -#Tue Mar 17 19:55:50 CET 2026 +#Wed Mar 18 15:28:58 CET 2026 display=\:0 host=Mario-Linux -process-id=148721 +process-id=26624 user=mario diff --git a/.metadata/.log b/.metadata/.log index 68ec10f..36798dc 100644 --- a/.metadata/.log +++ b/.metadata/.log @@ -1097,3 +1097,973 @@ Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git at java.base/java.io.FileInputStream.(FileInputStream.java:106) at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) ... 68 more + +!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-17 23:01:40.053 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-17 23:01:40.298 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) +!SESSION 2026-03-18 15:28:52.788 ----------------------------------------------- +eclipse.buildId=4.39.0.20260305-0817 +java.version=21.0.10 +java.vendor=Eclipse Adoptium +BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=de_DE +Framework arguments: -product org.eclipse.epp.package.java.product +Command-line arguments: -os linux -ws gtk -arch x86_64 -product org.eclipse.epp.package.java.product + +!ENTRY ch.qos.logback.classic 1 0 2026-03-18 15:28:53.799 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-18 15:28:58.414 +!MESSAGE Logback config file: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.m2e.logback/logback.2.7.101.20251017-1242.xml + +!ENTRY org.eclipse.ui 2 0 2026-03-18 15:28:58.572 +!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points. +!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-18 15:28:58.572 +!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory' + +!ENTRY org.eclipse.ui 2 0 2026-03-18 15:28:58.702 +!MESSAGE Warnings while parsing the commands from the 'org.eclipse.ui.commands' and 'org.eclipse.ui.actionDefinitions' extension points. +!SUBENTRY 1 org.eclipse.ui 2 0 2026-03-18 15:28:58.702 +!MESSAGE Commands should really have a category: plug-in='org.springframework.tooling.boot.ls', id='spring.initializr.addStarters', categoryId='org.eclipse.lsp4e.commandCategory' + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 16:50:37.017 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 17:25:44.906 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 17:33:38.648 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 17:35:19.625 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:02:17.714 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jface 2 0 2026-03-18 18:11:03.312 +!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. +!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-18 18:11:03.312 +!MESSAGE A conflict occurred for CTRL+SHIFT+T: +Binding(CTRL+SHIFT+T, + ParameterizedCommand(Command(org.eclipse.jdt.ui.navigate.open.type,Open Type, + Open a type in a Java editor, + Category(org.eclipse.ui.category.navigate,Navigate,null,true), + WorkbenchHandlerServiceHandler("org.eclipse.jdt.ui.navigate.open.type"), + ,,true),null), + org.eclipse.ui.defaultAcceleratorConfiguration, + org.eclipse.ui.contexts.window,,,system) +Binding(CTRL+SHIFT+T, + ParameterizedCommand(Command(org.eclipse.lsp4e.symbolInWorkspace,Go to Symbol in Workspace, + , + Category(org.eclipse.lsp4e.category,Language Servers,null,true), + WorkbenchHandlerServiceHandler("org.eclipse.lsp4e.symbolInWorkspace"), + ,,true),null), + org.eclipse.ui.defaultAcceleratorConfiguration, + org.eclipse.ui.contexts.window,,,system) + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-18 18:12:46.286 +!MESSAGE Javadoc unavailable. Failed to obtain it. +!STACK 0 +java.lang.InterruptedException + at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:386) + at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2096) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.getHoverInfo2(LSJavaHoverProvider.java:66) + at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:165) + at org.eclipse.jdt.internal.ui.text.java.hover.BestMatchHover.getHoverInfo2(BestMatchHover.java:131) + at org.eclipse.jdt.internal.ui.text.java.hover.JavaEditorTextHoverProxy.getHoverInfo2(JavaEditorTextHoverProxy.java:89) + at org.eclipse.jface.text.TextViewerHoverManager$1.run(TextViewerHoverManager.java:155) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:12:57.074 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:13:45.585 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:25:16.508 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:46:49.386 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:48:21.541 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:48:31.516 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:49:49.718 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 18:57:46.512 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:16:53.710 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:30:00.005 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 19:30:35.497 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:32:18.384 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:32:45.429 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-18 19:33:19.167 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:34:29.072 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:39:46.763 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:46:32.144 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:50:39.778 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 19:57:10.404 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:08:07.916 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:08:34.316 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 20:50:39.209 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 21:02:50.177 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 21:02:51.971 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:02:40.121 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:21:25.529 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:34:27.436 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) + +!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-18 22:34:35.489 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS + +!ENTRY org.eclipse.jdt.core 4 4 2026-03-18 22:34:35.761 +!MESSAGE Failed to save JDT index: Index for /xxxthegame +!STACK 0 +java.io.FileNotFoundException: /home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core/9341915.index (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at org.eclipse.jdt.internal.core.index.FileIndexLocation.getInputStream(FileIndexLocation.java:83) + at org.eclipse.jdt.internal.core.index.DiskIndex.readAllDocumentNames(DiskIndex.java:633) + at org.eclipse.jdt.internal.core.index.DiskIndex.mergeWith(DiskIndex.java:536) + at org.eclipse.jdt.internal.core.index.Index.save(Index.java:229) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndex(IndexManager.java:1135) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.saveIndexes(IndexManager.java:1178) + at org.eclipse.jdt.internal.core.search.indexing.IndexManager.notifyIdle(IndexManager.java:822) + at org.eclipse.jdt.internal.core.search.processing.JobManager.indexerLoop(JobManager.java:508) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame b/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame index cfd7476..6135611 100644 --- a/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame +++ b/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame @@ -1,8 +1,8 @@ # -#Tue Mar 17 19:55:42 CET 2026 +#Wed Mar 18 22:34:35 CET 2026 buildDir=build buildScriptPath=build.gradle.kts -classpath=\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n +classpath=\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n derivedResources=.gradle\:build gradleVersion=8.9 hasAutoBuildTasks=false diff --git a/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers b/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers index b1de59c..cdf2748 100644 Binary files a/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers and b/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources index 5bfddb9..f77c266 100644 Binary files a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources and b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ diff --git a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi index caf1cec..cdfba55 100644 --- a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi +++ b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -1,19 +1,19 @@ - - + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration - + - + topLevel shellMaximized - - - + + + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation persp.actionSet:org.eclipse.ui.cheatsheets.actionSet @@ -77,133 +77,127 @@ persp.newWizSC:org.eclipse.jdt.junit.wizards.NewTestCaseCreationWizard persp.actionSet:org.eclipse.jdt.junit.JUnitActionSet persp.viewSC:org.eclipse.ant.ui.views.AntView + persp.actionSet:org.eclipse.debug.ui.debugActionSet persp.editorOnboardingImageUri:platform:/plugin/org.eclipse.jdt.ui/$nl$/icons/full/onboarding_jperspective.svg persp.editorOnboardingText:Open a file or drop files here to open them. persp.editorOnboardingCommand:Find Actions$$$Ctrl+3 persp.editorOnboardingCommand:Show Key Assist$$$Shift+Ctrl+L persp.editorOnboardingCommand:New$$$Ctrl+N persp.editorOnboardingCommand:Open Type$$$Shift+Ctrl+T - persp.actionSet:org.eclipse.debug.ui.debugActionSet - - - - + + + + org.eclipse.e4.primaryNavigationStack active - noFocus - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:Java - - + + View categoryTag:Other - - + + View categoryTag:Git - - - - + + + + org.eclipse.e4.secondaryNavigationStack - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Mylyn - + View categoryTag:Java - + View categoryTag:Ant - + org.eclipse.e4.secondaryDataStack Oomph Gradle Debug - + View categoryTag:General - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Terminal - + View categoryTag:Gradle - + View categoryTag:Gradle - - View - categoryTag:Oomph - NoRestore - - + View categoryTag:Debug busy @@ -212,7 +206,7 @@ - + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation persp.actionSet:org.eclipse.ui.cheatsheets.actionSet @@ -261,100 +255,100 @@ persp.editorOnboardingCommand:Step Over$$$F6 persp.editorOnboardingCommand:Step Return$$$F7 persp.editorOnboardingCommand:Resume$$$F8 - - + + org.eclipse.e4.primaryNavigationStack - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:Java active - + View categoryTag:Java - + View categoryTag:Java - - - - + + + + org.eclipse.e4.secondaryNavigationStack - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Ant - - + + View categoryTag:General - + View categoryTag:General - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Terminal - + View categoryTag:Debug - + View categoryTag:General @@ -363,2640 +357,2603 @@ - - + + View categoryTag:Help - + View categoryTag:General - + View categoryTag:Help - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:Help - - + + EditorStack org.eclipse.e4.primaryDataStack - - - Editor - removeOnHide - org.eclipse.jdt.ui.ClassFileEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - Editor - removeOnHide - org.eclipse.compare.CompareEditor - - + View categoryTag:Java active - + activeOnClose + ViewMenu menuContribution:menu - + - + View categoryTag:Java - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Mylyn - + View categoryTag:Terminal - + View categoryTag:Java - + View categoryTag:Git - + View categoryTag:Java - + View categoryTag:Other - + ViewMenu menuContribution:menu - + - + View categoryTag:Ant - + View categoryTag:Gradle - + ViewMenu menuContribution:menu - + - + View categoryTag:Gradle - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - busy - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - - - - - View - categoryTag:Oomph - NoRestore - - ViewMenu - menuContribution:menu - - - - - + + toolbarSeparator - + - + Draggable - + - + toolbarSeparator - + - + Draggable - - + + - + toolbarSeparator - + - + Draggable - + Draggable - + Draggable - + Draggable - + toolbarSeparator - + - + Draggable - + - - Draggable - - - Draggable - - - Draggable - - + toolbarSeparator - + - + toolbarSeparator - + - + Draggable - + stretch SHOW_RESTORE_MENU - + Draggable HIDEABLE SHOW_RESTORE_MENU - - + + stretch - + Draggable - + + Draggable + + + + + TrimStack + Draggable + + + + + TrimStack + Draggable + + + TrimStack Draggable - - - - - - - - - - - - - - - + + + + + + + + + + + + + platform:gtk - - - - + + + + platform:gtk - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + + - - + + - - - - - - - - - + + + + + + + + + - - + + - - - + + + - - - - - + + + + + - - + + - - - + + + - - - + + + - - - - - - - - + + + + + + + + platform:gtk - - - - - + + + + + - - + + - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + - - - - + + + + - - - - - - - - + + + + + + + + - - + + - - - - - - + + + + + + - - - - - - + + + + + + - - + + - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor removeOnHide - + View categoryTag:Ant - + View categoryTag:Gradle - + View categoryTag:Gradle - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Java - + View categoryTag:Git - + View categoryTag:Git - + View categoryTag:Git - + View categoryTag:Git NoRestore - + View categoryTag:Git - + View categoryTag:Help - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Debug - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Language Servers - + View categoryTag:Language Servers - + View categoryTag:Language Servers - + View categoryTag:Maven - + View categoryTag:Maven - + View categoryTag:Maven - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Oomph - + View categoryTag:Oomph NoRestore - + View categoryTag:Plug-in Development - + View categoryTag:General - + View categoryTag:Version Control (Team) - + View categoryTag:Version Control (Team) - + View categoryTag:Terminal - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Spring - + View categoryTag:Spring - + View categoryTag:Spring - - + + glue move_after:PerspectiveSpacer SHOW_RESTORE_MENU - + move_after:Spacer Glue HIDEABLE SHOW_RESTORE_MENU - + glue move_after:SearchField SHOW_RESTORE_MENU - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index b/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index index cee2627..0da28b0 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index and b/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache index 2e4cff8..8f63c03 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache and b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps index d9306b8..792fd75 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps and b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache index b11ed97..b685e20 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache and b/.metadata/.plugins/org.eclipse.jdt.core/nonChainingJarsCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt index fe69f8c..1384d64 100644 --- a/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt +++ b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt @@ -1,105 +1,106 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.eclipse.jdt.core -2628068441.index -2403041570.index -1704193220.index -3051047092.index -2466743981.index -2852275968.index -3371017197.index -2376429633.index -2236377038.index -833027591.index -4080887926.index -3424266581.index -2488355463.index -3514351073.index -1090991043.index -13999064.index -352173590.index -2799433906.index -3718169413.index -2157310342.index -1730373086.index -341080888.index -3839581777.index -3572667491.index -2826242951.index -2874180664.index -134995224.index -2455962971.index -1063231598.index -3769604005.index 794464160.index -1067882983.index -3547251881.index -1022297761.index +2655170954.index +176453541.index +677104696.index +341080888.index +774576701.index 4134502745.index -1780956574.index -369020172.index +41199409.index +134995224.index 2217896880.index +4025319337.index +900586112.index +2929476459.index +2065500052.index +3051047092.index +815902026.index +3718169413.index +721517855.index +369020172.index +3899935016.index +2157310342.index +2488355463.index +3572667491.index +2799433906.index +675283020.index +2032345814.index +3839581777.index +2466743981.index +13999064.index +673436610.index +3972616808.index +1914043487.index +3154281632.index +1117161889.index +983587063.index +766461225.index +286641703.index +3371017197.index +4080887926.index +2941512597.index +1730373086.index +3882180612.index +4020783879.index +2900482015.index +3059431983.index +833027591.index +13156219.index +4088356365.index +37241354.index +1295630681.index +2701419231.index +3939420913.index +1067882983.index +1318022262.index +773718761.index +2311226047.index +3539841425.index +1865797976.index +2455962971.index +836138551.index +2389383899.index +2226615777.index +3515611559.index +3728851734.index +2826242951.index +2899155238.index +3763224039.index +2138052223.index +2236377038.index +3547251881.index 371677185.index 2127778675.index -2389383899.index -2701419231.index +2519831052.index +1063231598.index +2874180664.index +2939623059.index +2576972120.index +2376429633.index +2628068441.index +1090991043.index +1138623861.index +1223891870.index +3769604005.index +3158780236.index +2237645717.index +2852275968.index +2403041570.index +1704193220.index +2004806901.index +3952767374.index 3416862923.index 3912907421.index -1256436118.index -815902026.index -900586112.index -766461225.index -1117161889.index -675283020.index -4088356365.index -836138551.index -2226615777.index -3539841425.index -2939623059.index -3728851734.index -3972616808.index -2494834982.index -1938594271.index -4025319337.index 781064456.index -2032345814.index -2655170954.index -983587063.index -3939420913.index -2247053514.index -1138623861.index -3882180612.index -2237645717.index -721517855.index -176453541.index -4020783879.index -3899935016.index -2576972120.index -1223891870.index -3158780236.index -677104696.index +352173590.index 766439048.index -41199409.index -2900482015.index -3952767374.index -773718761.index -2519831052.index -286641703.index -3515611559.index -1865797976.index -3059431983.index -2929476459.index -774576701.index -13156219.index -2311226047.index -2138052223.index -3763224039.index -3154281632.index -1318022262.index -2065500052.index -37241354.index -2899155238.index -673436610.index -1914043487.index -1295630681.index -2941512597.index +3424266581.index +2247053514.index +1765772496.index +3514351073.index 3892622621.index -2004806901.index +2494834982.index +1780956574.index +1022297761.index +1938594271.index +1256436118.index diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml index 37c217c..aa899f1 100644 --- a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml +++ b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml @@ -18,4 +18,5 @@ + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml index 271e066..f904cf6 100644 --- a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml +++ b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -24,6 +24,10 @@
+ + + +
@@ -68,4 +72,13 @@
+
+ + + + +
+
+ +
diff --git a/.metadata/.plugins/org.eclipse.m2e.logback/0.log b/.metadata/.plugins/org.eclipse.m2e.logback/0.log index e7ed630..819a1c0 100644 --- a/.metadata/.plugins/org.eclipse.m2e.logback/0.log +++ b/.metadata/.plugins/org.eclipse.m2e.logback/0.log @@ -21,3 +21,4 @@ 2026-03-17 19:39:55,931 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is out-of-date. Trying to update. 2026-03-17 19:49:51,508 [Worker-2: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. 2026-03-17 19:55:53,050 [Worker-1: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. +2026-03-18 15:29:01,371 [Worker-7: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. diff --git a/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml index e074d67..a736a11 100644 --- a/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml +++ b/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml @@ -12,7 +12,7 @@ - +
diff --git a/.metadata/version.ini b/.metadata/version.ini index 57cce48..5c3b5a0 100644 --- a/.metadata/version.ini +++ b/.metadata/version.ini @@ -1,3 +1,3 @@ -#Tue Mar 17 19:55:50 CET 2026 +#Wed Mar 18 15:28:58 CET 2026 org.eclipse.core.runtime=2 org.eclipse.platform=4.39.0.v20260226-0420 diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/Finisher.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/Finisher.java index 363b15d..4df7e27 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/Finisher.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/Finisher.java @@ -1,6 +1,6 @@ package de.oaa.xxx.aufgaben; -import de.oaa.xxx.session.GeschlechtEnum; +import de.oaa.xxx.games.bdsm.GeschlechtEnum; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabeEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabeEntity.java index 090ebd8..0b1f491 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabeEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabeEntity.java @@ -67,9 +67,9 @@ public class AufgabeEntity { public Aufgabe toAufgabe() { Aufgabe aufgabe = new Aufgabe(); aufgabe.setAufgabeId(aufgabeId); - aufgabe.setBenoetigtAktiv(benoetigtAktiv); - aufgabe.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList()); - aufgabe.setBenoetigtPassiv(benoetigtPassiv); + aufgabe.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>()); + aufgabe.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>()); + aufgabe.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>()); aufgabe.setGruppeId(aufgabenGruppe.getGruppenId()); aufgabe.setKurzText(kurzText); aufgabe.setLevel(level); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabenGruppeEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabenGruppeEntity.java index 69da27b..1f1fb00 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabenGruppeEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/AufgabenGruppeEntity.java @@ -18,7 +18,7 @@ import java.util.UUID; @Getter @Setter @Entity -@Table(name = "aufgabenGruppe") +@Table(name = "aufgaben_gruppe") public class AufgabenGruppeEntity { @Id diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/FinisherEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/FinisherEntity.java index 3d54c0a..b4b7c55 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/FinisherEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/FinisherEntity.java @@ -2,7 +2,7 @@ package de.oaa.xxx.aufgaben.entity; import de.oaa.xxx.aufgaben.Finisher; import de.oaa.xxx.aufgaben.Werkzeug; -import de.oaa.xxx.session.GeschlechtEnum; +import de.oaa.xxx.games.bdsm.GeschlechtEnum; import jakarta.persistence.CascadeType; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; @@ -67,9 +67,9 @@ public class FinisherEntity { finisher.setKurzText(kurzText); finisher.setText(text); finisher.setGeschlecht(geschlecht); - finisher.setBenoetigtAktiv(benoetigtAktiv); - finisher.setBenoetigtPassiv(benoetigtPassiv); - finisher.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList()); + finisher.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>()); + finisher.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>()); + finisher.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>()); finisher.setGruppeId(aufgabenGruppe.getGruppenId()); return finisher; } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/SperreEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/SperreEntity.java index e8150dd..10fc49f 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/SperreEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/SperreEntity.java @@ -67,7 +67,7 @@ public class SperreEntity { sperre.setMinutenBis(minutenBis); sperre.setMinutenVon(minutenVon); sperre.setReleaseText(releaseText); - sperre.setSperreFuer(sperreFuer); + sperre.setSperreFuer(sperreFuer != null ? new ArrayList<>(sperreFuer) : new ArrayList<>()); sperre.setText(text); sperre.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>()); return sperre; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/StrafeEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/StrafeEntity.java index 6bbedbf..b97d64b 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/StrafeEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/aufgaben/entity/StrafeEntity.java @@ -67,9 +67,9 @@ public class StrafeEntity { public Strafe toStrafe() { Strafe strafe = new Strafe(); strafe.setStrafeId(strafeId); - strafe.setBenoetigtAktiv(benoetigtAktiv); - strafe.setBenoetigteToys(benoetigteToys.stream().map(ToyEntity::toToy).toList()); - strafe.setBenoetigtPassiv(benoetigtPassiv); + strafe.setBenoetigtAktiv(benoetigtAktiv != null ? new ArrayList<>(benoetigtAktiv) : new ArrayList<>()); + strafe.setBenoetigteToys(benoetigteToys != null ? benoetigteToys.stream().map(ToyEntity::toToy).toList() : new ArrayList<>()); + strafe.setBenoetigtPassiv(benoetigtPassiv != null ? new ArrayList<>(benoetigtPassiv) : new ArrayList<>()); strafe.setGruppeId(aufgabenGruppe.getGruppenId()); strafe.setKurzText(kurzText); strafe.setLevel(level); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/AktiveSperre.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AktiveSperre.java similarity index 92% rename from xxxthegame/src/main/java/de/oaa/xxx/session/AktiveSperre.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AktiveSperre.java index 6a203b0..a19c7e8 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/AktiveSperre.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AktiveSperre.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeAnzeige.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeAnzeige.java similarity index 81% rename from xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeAnzeige.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeAnzeige.java index 29dd0c2..5972b6d 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeAnzeige.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeAnzeige.java @@ -1,21 +1,25 @@ -package de.oaa.xxx.session; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class AufgabeAnzeige { - - private String nameAktiverMitspieler; - private String aufgabeText; - private Integer timer; - private Callback callback; - private Integer level; - - @Override - public String toString() { - return "AufgabeAnzeige[mitspieler=" + nameAktiverMitspieler + ", level=" + level + ", timer=" + timer - + ", callback=" + (callback != null ? callback.getClass().getSimpleName() : null) + "]"; - } -} +package de.oaa.xxx.games.bdsm; + +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +public class AufgabeAnzeige { + + private String nameAktiverMitspieler; + private String aufgabeText; + private Integer timer; + private Callback callback; + private Integer level; + private UUID mitspielerId; + private boolean eigenesGeraet; + + @Override + public String toString() { + return "AufgabeAnzeige[mitspieler=" + nameAktiverMitspieler + ", level=" + level + ", timer=" + timer + + ", callback=" + (callback != null ? callback.getClass().getSimpleName() : null) + "]"; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeArt.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeArt.java similarity index 63% rename from xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeArt.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeArt.java index 1af07df..2444f20 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/AufgabeArt.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/AufgabeArt.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; public enum AufgabeArt { AUFGABE, diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/Session.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGame.java similarity index 88% rename from xxxthegame/src/main/java/de/oaa/xxx/session/Session.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGame.java index 13e36bf..7ac64a4 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/Session.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGame.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; import lombok.Getter; import lombok.Setter; @@ -8,10 +8,11 @@ import java.util.UUID; @Getter @Setter -public class Session { +public class BdsmGame { private UUID sessionId; private UUID userId; + private UUID setupId; private Integer wahrscheinlichkeitSperre; private Integer wahrscheinlichkeitStrafe; private Integer aufgabenProLevel; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/SessionDurchfuehren.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameDurchfuehren.java similarity index 91% rename from xxxthegame/src/main/java/de/oaa/xxx/session/SessionDurchfuehren.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameDurchfuehren.java index 5704f94..e3c7941 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/SessionDurchfuehren.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameDurchfuehren.java @@ -1,251 +1,265 @@ -package de.oaa.xxx.session; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import de.oaa.xxx.session.aufgaben.Aufgabe; -import de.oaa.xxx.session.aufgaben.AufgabenList; -import de.oaa.xxx.session.aufgaben.Sperre; -import de.oaa.xxx.session.aufgaben.Strafe; -import de.oaa.xxx.session.entity.SessionEntity; -import de.oaa.xxx.session.sperre.SperreCallback; -import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback; - -public class SessionDurchfuehren { - - private final AufgabenList aufgabenList; - private final List mitspieler = new ArrayList<>(); - private final List aktiveSperren = new ArrayList<>(); - - private final Integer wahrscheinlichkeitSperre; - private final Integer wahrscheinlichkeitStrafe; - - private int aufgabenProLevel; - private int level; - private int aufgabenAufAktuellemLevel; - - public SessionDurchfuehren(SessionEntity entity) throws Exception { - ObjectMapper objectMapper = new ObjectMapper(); - aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class); - entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler())); - entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler))); - - wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre(); - wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe(); - - this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5; - this.level = entity.getLevel() != null ? entity.getLevel() : 1; - this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0; - } - - public AufgabeAnzeige getNext() { - checkLevel(); - if (level == 6) { - return null; - } - AufgabeAnzeige anzeige = null; - int nextInt = new Random().nextInt(1, 100); - if (nextInt == 1) { - anzeige = findUltimativeStrafe(); - } else if (nextInt == 2) { - anzeige = findSperreVerlaengern(); - } else if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) { - anzeige = findeAufgabe(); - } else if (nextInt > wahrscheinlichkeitSperre + 2) { - anzeige = findeStrafe(); - } else { - anzeige = findeSperre(); - } - if (anzeige == null) { - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); - Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); - String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting."; - anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : ""); - anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?")); - anzeige.setTimer(120); - } - return anzeige; - } - - public void backToLvl5() { - this.level = 5; - this.aufgabenAufAktuellemLevel = 0; - } - - public List getFinisher() { - var list = new ArrayList(); - List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> { - mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> { - var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming); - var finishers = aufgabenList.getFinisher().stream() - .filter(finisher -> geschlecht == finisher.getGeschlecht()) - .toList(); - if (!finishers.isEmpty()) { - var aufgabe = finishers.get(new Random().nextInt(list.size())); - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(cumming.getName()); - anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), - cumming.getName(), partner != null ? partner.getName() : "")); - list.add(anzeige); - } - }); - }); - return list; - } - - private void checkLevel() { - if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) { - aufgabenAufAktuellemLevel = 0; - level++; - } - } - - private AufgabeAnzeige findUltimativeStrafe() { - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); - if (aktiv != null) { - Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); - if (passiv != null) { - String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest."; - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(aktiv.getName()); - anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName())); - anzeige.setTimer(new Random().nextInt(1800, 7200)); - return anzeige; - } - } - return findeStrafe(); - } - - private AufgabeAnzeige findSperreVerlaengern() { - if (!aktiveSperren.isEmpty()) { - AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size())); - Mitspieler passiv = sperre.getMitspieler(); - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv); - if (aktiv != null) { - String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?"; - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName())); - anzeige.setNameAktiverMitspieler(aktiv.getName()); - SperrenVerlaengernCallback callback = new SperrenVerlaengernCallback(); - callback.setFaktor(new Random().nextInt(2, 4)); - callback.setSpielerId(passiv.getId()); - anzeige.setCallback(callback); - return anzeige; - } - } - return findeSperre(); - } - - private AufgabeAnzeige findeAufgabe() { - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); - if (aktiv != null) { - Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); - if (passiv != null) { - List list = aufgabenList.getAufgaben().stream() - .filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv)) - .collect(Collectors.toList()); - if (!list.isEmpty()) { - Aufgabe aufgabe = list.get(new Random().nextInt(list.size())); - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(aktiv.getName()); - anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), aktiv.getName(), passiv.getName())); - if (aufgabe.getSekundenVon() != null) { - if (aufgabe.getSekundenBis() != null) { - anzeige.setTimer(new Random().nextInt(aufgabe.getSekundenVon(), aufgabe.getSekundenBis())); - } else { - anzeige.setTimer(aufgabe.getSekundenVon()); - } - } - return anzeige; - } - } - } - return findeStrafe(); - } - - private AufgabeAnzeige findeStrafe() { - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); - if (aktiv != null) { - Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); - if (passiv != null) { - List list = aufgabenList.getStrafen().stream() - .filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv)) - .collect(Collectors.toList()); - if (!list.isEmpty()) { - Strafe strafe = list.get(new Random().nextInt(list.size())); - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(aktiv.getName()); - anzeige.setAufgabeText(getAnzeigeText(strafe.getText(), aktiv.getName(), passiv.getName())); - if (strafe.getSekundenVon() != null) { - if (strafe.getSekundenBis() != null) { - anzeige.setTimer(new Random().nextInt(strafe.getSekundenVon(), strafe.getSekundenBis())); - } else { - anzeige.setTimer(strafe.getSekundenVon()); - } - } - return anzeige; - } - } - } - return findeSperre(); - } - - private AufgabeAnzeige findeSperre() { - Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); - if (aktiv != null) { - Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); - if (passiv != null) { - List list = aufgabenList.getSperren().stream() - .filter(sperre -> sperre.isAufgabePassend(passiv)) - .collect(Collectors.toList()); - if (!list.isEmpty()) { - Sperre sperre = list.get(new Random().nextInt(list.size())); - AufgabeAnzeige anzeige = new AufgabeAnzeige(); - anzeige.setNameAktiverMitspieler(aktiv.getName()); - anzeige.setAufgabeText(getAnzeigeText(sperre.getText(), aktiv.getName(), passiv.getName())); - SperreCallback callback = new SperreCallback(); - callback.setSperreId(sperre.getSperreId()); - callback.setSpielerId(passiv.getId()); - callback.setReleaseText(getAnzeigeText(sperre.getReleaseText(), aktiv.getName(), passiv.getName())); - anzeige.setCallback(callback); - return anzeige; - } - } - } - return null; - } - - private String getAnzeigeText(String textMitPlatzhaltern, String nameAktiv, String namePassiv) { - return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv); - } - - private Mitspieler findeMitspielerMitRolle(RolleEnum rolle) { - List list = mitspieler.stream() - .filter(m -> m.getRollen().contains(rolle)) - .toList(); - return list.isEmpty() ? null : list.get(new Random().nextInt(list.size())); - } - - private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) { - if (gegenspieler == null) return findeMitspielerMitRolle(rolle); - List list = mitspieler.stream() - .filter(m -> m != gegenspieler) - .filter(m -> m.isPassenderSpielpartner(gegenspieler)) - .filter(m -> m.getRollen().contains(rolle)) - .toList(); - return list.isEmpty() ? null : list.get(new Random().nextInt(list.size())); - } - - public int getAufgabenAufAktuellemLevel() { - return aufgabenAufAktuellemLevel; - } - - public int getLevel() { - return level; - } -} +package de.oaa.xxx.games.bdsm; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.oaa.xxx.games.bdsm.aufgaben.Aufgabe; +import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList; +import de.oaa.xxx.games.bdsm.aufgaben.Sperre; +import de.oaa.xxx.games.bdsm.aufgaben.Strafe; +import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity; +import de.oaa.xxx.games.bdsm.sperre.SperreCallback; +import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback; + +public class BdsmGameDurchfuehren { + + private final AufgabenList aufgabenList; + private final List mitspieler = new ArrayList<>(); + private final List aktiveSperren = new ArrayList<>(); + + private final Integer wahrscheinlichkeitSperre; + private final Integer wahrscheinlichkeitStrafe; + + private int aufgabenProLevel; + private int level; + private int aufgabenAufAktuellemLevel; + + public BdsmGameDurchfuehren(BdsmGameEntity entity) throws Exception { + ObjectMapper objectMapper = new ObjectMapper(); + aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class); + entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler())); + entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler))); + + wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre(); + wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe(); + + this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5; + this.level = entity.getLevel() != null ? entity.getLevel() : 1; + this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0; + } + + public AufgabeAnzeige getNext() { + checkLevel(); + if (level == 6) { + return null; + } + AufgabeAnzeige anzeige = null; + int nextInt = new Random().nextInt(1, 100); + if (nextInt == 1) { + anzeige = findUltimativeStrafe(); + } else if (nextInt == 2) { + anzeige = findSperreVerlaengern(); + } else if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) { + anzeige = findeAufgabe(); + } else if (nextInt > wahrscheinlichkeitSperre + 2) { + anzeige = findeStrafe(); + } else { + anzeige = findeSperre(); + } + if (anzeige == null) { + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); + Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); + String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting."; + anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : ""); + setMitspielerInfo(anzeige, aktiv); + anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?")); + anzeige.setTimer(120); + } + return anzeige; + } + + public void backToLvl5() { + this.level = 5; + this.aufgabenAufAktuellemLevel = 0; + } + + public List getFinisher() { + var list = new ArrayList(); + List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> { + mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> { + var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming); + var finishers = aufgabenList.getFinisher().stream() + .filter(finisher -> geschlecht == finisher.getGeschlecht()) + .toList(); + if (!finishers.isEmpty()) { + var aufgabe = finishers.get(new Random().nextInt(list.size())); + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(cumming.getName()); + setMitspielerInfo(anzeige, cumming); + anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), + cumming.getName(), partner != null ? partner.getName() : "")); + list.add(anzeige); + } + }); + }); + return list; + } + + private void checkLevel() { + if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) { + aufgabenAufAktuellemLevel = 0; + level++; + } + } + + private void setMitspielerInfo(AufgabeAnzeige anzeige, Mitspieler aktiv) { + if (aktiv != null) { + anzeige.setMitspielerId(aktiv.getId()); + anzeige.setEigenesGeraet(aktiv.isEigenesGeraet()); + } + } + + private AufgabeAnzeige findUltimativeStrafe() { + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); + if (aktiv != null) { + Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); + if (passiv != null) { + String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest."; + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(aktiv.getName()); + setMitspielerInfo(anzeige, aktiv); + anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName())); + anzeige.setTimer(new Random().nextInt(1800, 7200)); + return anzeige; + } + } + return findeStrafe(); + } + + private AufgabeAnzeige findSperreVerlaengern() { + if (!aktiveSperren.isEmpty()) { + AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size())); + Mitspieler passiv = sperre.getMitspieler(); + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv); + if (aktiv != null) { + String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?"; + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName())); + anzeige.setNameAktiverMitspieler(aktiv.getName()); + setMitspielerInfo(anzeige, aktiv); + SperrenVerlaengernCallback callback = new SperrenVerlaengernCallback(); + callback.setFaktor(new Random().nextInt(2, 4)); + callback.setSpielerId(passiv.getId()); + anzeige.setCallback(callback); + return anzeige; + } + } + return findeSperre(); + } + + private AufgabeAnzeige findeAufgabe() { + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV); + if (aktiv != null) { + Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv); + if (passiv != null) { + List list = aufgabenList.getAufgaben().stream() + .filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv)) + .collect(Collectors.toList()); + if (!list.isEmpty()) { + Aufgabe aufgabe = list.get(new Random().nextInt(list.size())); + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(aktiv.getName()); + setMitspielerInfo(anzeige, aktiv); + anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), aktiv.getName(), passiv.getName())); + if (aufgabe.getSekundenVon() != null) { + if (aufgabe.getSekundenBis() != null) { + anzeige.setTimer(new Random().nextInt(aufgabe.getSekundenVon(), aufgabe.getSekundenBis())); + } else { + anzeige.setTimer(aufgabe.getSekundenVon()); + } + } + return anzeige; + } + } + } + return findeStrafe(); + } + + private AufgabeAnzeige findeStrafe() { + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); + if (aktiv != null) { + Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); + if (passiv != null) { + List list = aufgabenList.getStrafen().stream() + .filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv)) + .collect(Collectors.toList()); + if (!list.isEmpty()) { + Strafe strafe = list.get(new Random().nextInt(list.size())); + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(aktiv.getName()); + setMitspielerInfo(anzeige, aktiv); + anzeige.setAufgabeText(getAnzeigeText(strafe.getText(), aktiv.getName(), passiv.getName())); + if (strafe.getSekundenVon() != null) { + if (strafe.getSekundenBis() != null) { + anzeige.setTimer(new Random().nextInt(strafe.getSekundenVon(), strafe.getSekundenBis())); + } else { + anzeige.setTimer(strafe.getSekundenVon()); + } + } + return anzeige; + } + } + } + return findeSperre(); + } + + private AufgabeAnzeige findeSperre() { + Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV); + if (aktiv != null) { + Mitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv); + if (passiv != null) { + List list = aufgabenList.getSperren().stream() + .filter(sperre -> sperre.isAufgabePassend(passiv)) + .collect(Collectors.toList()); + if (!list.isEmpty()) { + Sperre sperre = list.get(new Random().nextInt(list.size())); + AufgabeAnzeige anzeige = new AufgabeAnzeige(); + anzeige.setNameAktiverMitspieler(aktiv.getName()); + setMitspielerInfo(anzeige, aktiv); + anzeige.setAufgabeText(getAnzeigeText(sperre.getText(), aktiv.getName(), passiv.getName())); + SperreCallback callback = new SperreCallback(); + callback.setSperreId(sperre.getSperreId()); + callback.setSpielerId(passiv.getId()); + callback.setReleaseText(getAnzeigeText(sperre.getReleaseText(), aktiv.getName(), passiv.getName())); + anzeige.setCallback(callback); + return anzeige; + } + } + } + return null; + } + + private String getAnzeigeText(String textMitPlatzhaltern, String nameAktiv, String namePassiv) { + return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv); + } + + private Mitspieler findeMitspielerMitRolle(RolleEnum rolle) { + List list = mitspieler.stream() + .filter(m -> m.getRollen().contains(rolle)) + .toList(); + return list.isEmpty() ? null : list.get(new Random().nextInt(list.size())); + } + + private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) { + if (gegenspieler == null) return findeMitspielerMitRolle(rolle); + List list = mitspieler.stream() + .filter(m -> m != gegenspieler) + .filter(m -> m.isPassenderSpielpartner(gegenspieler)) + .filter(m -> m.getRollen().contains(rolle)) + .toList(); + return list.isEmpty() ? null : list.get(new Random().nextInt(list.size())); + } + + public int getAufgabenAufAktuellemLevel() { + return aufgabenAufAktuellemLevel; + } + + public int getLevel() { + return level; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/Callback.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Callback.java similarity index 88% rename from xxxthegame/src/main/java/de/oaa/xxx/session/Callback.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Callback.java index a116d45..0f83c59 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/Callback.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Callback.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; import java.util.UUID; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/GeschlechtEnum.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/GeschlechtEnum.java similarity index 65% rename from xxxthegame/src/main/java/de/oaa/xxx/session/GeschlechtEnum.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/GeschlechtEnum.java index ff5e173..7851012 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/GeschlechtEnum.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/GeschlechtEnum.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; public enum GeschlechtEnum { WEIBLICH, diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/Mitspieler.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Mitspieler.java similarity index 87% rename from xxxthegame/src/main/java/de/oaa/xxx/session/Mitspieler.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Mitspieler.java index fb23fa1..40ae751 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/Mitspieler.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Mitspieler.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; import lombok.Getter; import lombok.Setter; @@ -11,6 +11,8 @@ import java.util.UUID; public class Mitspieler { private UUID id; + private UUID userId; + private boolean eigenesGeraet; private String name; private GeschlechtEnum geschlecht; private List spieltMit; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/RolleEnum.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/RolleEnum.java similarity index 74% rename from xxxthegame/src/main/java/de/oaa/xxx/session/RolleEnum.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/RolleEnum.java index d53e8d7..cb19db6 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/RolleEnum.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/RolleEnum.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; public enum RolleEnum { BESTRAFUNG_AKTIV, diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/Werkzeug.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Werkzeug.java similarity index 93% rename from xxxthegame/src/main/java/de/oaa/xxx/session/Werkzeug.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Werkzeug.java index 0394fdd..0314551 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/Werkzeug.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/Werkzeug.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session; +package de.oaa.xxx.games.bdsm; public enum Werkzeug { diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Aufgabe.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Aufgabe.java similarity index 88% rename from xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Aufgabe.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Aufgabe.java index 4b2384e..307852f 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Aufgabe.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Aufgabe.java @@ -1,7 +1,7 @@ -package de.oaa.xxx.session.aufgaben; +package de.oaa.xxx.games.bdsm.aufgaben; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.Werkzeug; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.Werkzeug; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/AufgabenList.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/AufgabenList.java similarity index 89% rename from xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/AufgabenList.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/AufgabenList.java index c929ee4..6701e48 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/AufgabenList.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/AufgabenList.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session.aufgaben; +package de.oaa.xxx.games.bdsm.aufgaben; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Finisher.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Finisher.java similarity index 80% rename from xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Finisher.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Finisher.java index dd71013..11914b6 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Finisher.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Finisher.java @@ -1,7 +1,7 @@ -package de.oaa.xxx.session.aufgaben; +package de.oaa.xxx.games.bdsm.aufgaben; -import de.oaa.xxx.session.GeschlechtEnum; -import de.oaa.xxx.session.Werkzeug; +import de.oaa.xxx.games.bdsm.GeschlechtEnum; +import de.oaa.xxx.games.bdsm.Werkzeug; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Sperre.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Sperre.java similarity index 84% rename from xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Sperre.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Sperre.java index c2be31c..28fa8e4 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Sperre.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Sperre.java @@ -1,7 +1,7 @@ -package de.oaa.xxx.session.aufgaben; +package de.oaa.xxx.games.bdsm.aufgaben; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.Werkzeug; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.Werkzeug; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Strafe.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Strafe.java similarity index 87% rename from xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Strafe.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Strafe.java index 3fd1fbe..b1cb3ad 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/aufgaben/Strafe.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/aufgaben/Strafe.java @@ -1,7 +1,7 @@ -package de.oaa.xxx.session.aufgaben; +package de.oaa.xxx.games.bdsm.aufgaben; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.Werkzeug; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.Werkzeug; import lombok.Getter; import lombok.Setter; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmEinladungController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmEinladungController.java new file mode 100644 index 0000000..7a9c4e0 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmEinladungController.java @@ -0,0 +1,195 @@ +package de.oaa.xxx.games.bdsm.controller; + +import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status; +import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository; +import de.oaa.xxx.social.SystemMessageService; +import de.oaa.xxx.social.entity.MessageCause; +import de.oaa.xxx.social.repository.FriendshipRepository; +import de.oaa.xxx.user.UserRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; +import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RestController +@RequestMapping("/bdsm/einladung") +@Transactional +public class BdsmEinladungController { + + private final BdsmEinladungRepository einladungRepository; + private final UserRepository userRepository; + private final FriendshipRepository friendshipRepository; + private final SystemMessageService systemMessageService; + + public BdsmEinladungController(BdsmEinladungRepository einladungRepository, + UserRepository userRepository, + FriendshipRepository friendshipRepository, + SystemMessageService systemMessageService) { + this.einladungRepository = einladungRepository; + this.userRepository = userRepository; + this.friendshipRepository = friendshipRepository; + this.systemMessageService = systemMessageService; + } + + record EinladungRequest(UUID setupId, int slotIndex, UUID inviteeId) {} + record AntwortRequest(boolean accepted, String mode) {} // mode: OWN_DEVICE | HOST_DEVICE + + private UUID currentUserId(Principal principal) { + return userRepository.findByEmail(principal.getName()) + .map(u -> u.getUserId()).orElse(null); + } + + @PostMapping + public ResponseEntity> sendEinladung(@RequestBody EinladungRequest req, Principal principal) { + UUID inviterId = currentUserId(principal); + if (inviterId == null) return ResponseEntity.status(401).build(); + + // Freundschaft prüfen + var friendship = friendshipRepository.findExisting(inviterId, req.inviteeId()); + if (friendship.isEmpty() || friendship.get().getStatus() != de.oaa.xxx.social.entity.FriendshipEntity.Status.ACCEPTED) { + return ResponseEntity.status(403).build(); + } + + // Alte Einladung für diesen Slot canceln + einladungRepository.findBySetupId(req.setupId()).stream() + .filter(e -> e.getSlotIndex() == req.slotIndex() && e.getStatus() == Status.PENDING) + .forEach(e -> e.setStatus(Status.CANCELLED)); + + BdsmEinladungEntity entity = new BdsmEinladungEntity(); + entity.setEinladungId(UUID.randomUUID()); + entity.setSetupId(req.setupId()); + entity.setInviterId(inviterId); + entity.setInviteeId(req.inviteeId()); + entity.setSlotIndex(req.slotIndex()); + entity.setStatus(Status.PENDING); + entity.setCreatedAt(LocalDateTime.now()); + einladungRepository.save(entity); + + String inviterName = userRepository.findById(inviterId).map(u -> u.getName()).orElse("Jemand"); + systemMessageService.send( + inviterId, req.inviteeId(), + inviterName + " hat dich zum BDSM Game eingeladen.", + "/einladungen.html", + MessageCause.INVITATION + ); + + Map result = new LinkedHashMap<>(); + result.put("einladungId", entity.getEinladungId()); + return ResponseEntity.ok(result); + } + + @DeleteMapping("/{id}") + public ResponseEntity cancelEinladung(@PathVariable UUID id, Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null); + if (e == null) return ResponseEntity.notFound().build(); + if (!e.getInviterId().equals(userId)) return ResponseEntity.status(403).build(); + e.setStatus(Status.CANCELLED); + return ResponseEntity.accepted().build(); + } + + @GetMapping + public ResponseEntity>> getBySetupId(@RequestParam UUID setupId, Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + List> list = einladungRepository.findBySetupId(setupId).stream() + .map(this::toMap).toList(); + return ResponseEntity.ok(list); + } + + @GetMapping("/pending") + public ResponseEntity>> getPending(Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + List> list = einladungRepository.findByInviteeIdAndStatus(userId, Status.PENDING) + .stream().map(e -> { + Map m = toMap(e); + userRepository.findById(e.getInviterId()).ifPresent(u -> { + m.put("inviterName", u.getName()); + m.put("inviterAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : ""); + }); + return m; + }).toList(); + return ResponseEntity.ok(list); + } + + @GetMapping("/sent") + public ResponseEntity>> getSent(Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + List> list = einladungRepository.findByInviterIdAndStatus(userId, Status.PENDING) + .stream().map(e -> { + Map m = toMap(e); + userRepository.findById(e.getInviteeId()).ifPresent(u -> { + m.put("inviteeName", u.getName()); + m.put("inviteeAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : ""); + }); + return m; + }).toList(); + return ResponseEntity.ok(list); + } + + @GetMapping("/{id}") + public ResponseEntity> getById(@PathVariable UUID id, Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null); + if (e == null) return ResponseEntity.notFound().build(); + if (!e.getInviteeId().equals(userId) && !e.getInviterId().equals(userId)) { + return ResponseEntity.status(403).build(); + } + Map m = toMap(e); + userRepository.findById(e.getInviterId()).ifPresent(u -> { + m.put("inviterName", u.getName()); + m.put("inviterAvatar", u.getProfilePicture() != null ? u.getProfilePicture() : ""); + }); + return ResponseEntity.ok(m); + } + + @PutMapping("/{id}/antwort") + public ResponseEntity antwort(@PathVariable UUID id, @RequestBody AntwortRequest req, Principal principal) { + UUID userId = currentUserId(principal); + if (userId == null) return ResponseEntity.status(401).build(); + BdsmEinladungEntity e = einladungRepository.findById(id).orElse(null); + if (e == null) return ResponseEntity.notFound().build(); + if (!e.getInviteeId().equals(userId)) return ResponseEntity.status(403).build(); + if (e.getStatus() != Status.PENDING) return ResponseEntity.badRequest().build(); + if (!req.accepted()) { + e.setStatus(Status.DECLINED); + } else if ("OWN_DEVICE".equals(req.mode())) { + e.setStatus(Status.ACCEPTED_OWN); + } else { + e.setStatus(Status.ACCEPTED_HOST); + } + return ResponseEntity.accepted().build(); + } + + private Map toMap(BdsmEinladungEntity e) { + Map m = new LinkedHashMap<>(); + m.put("einladungId", e.getEinladungId()); + m.put("setupId", e.getSetupId()); + m.put("slotIndex", e.getSlotIndex()); + m.put("inviteeId", e.getInviteeId()); + m.put("inviterId", e.getInviterId()); + m.put("status", e.getStatus().name()); + m.put("sessionId", e.getSessionId()); + return m; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/controller/SessionController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmGameController.java similarity index 56% rename from xxxthegame/src/main/java/de/oaa/xxx/session/controller/SessionController.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmGameController.java index 6a78f21..ea747e4 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/controller/SessionController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/BdsmGameController.java @@ -1,16 +1,22 @@ -package de.oaa.xxx.session.controller; +package de.oaa.xxx.games.bdsm.controller; import com.fasterxml.jackson.databind.ObjectMapper; -import de.oaa.xxx.session.AufgabeAnzeige; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.Session; -import de.oaa.xxx.session.SessionDurchfuehren; -import de.oaa.xxx.session.aufgaben.AufgabenList; -import de.oaa.xxx.session.entity.MitspielerEntity; -import de.oaa.xxx.session.entity.SessionEntity; -import de.oaa.xxx.session.repository.AktiveSperreRepository; -import de.oaa.xxx.session.repository.MitspielerRepository; -import de.oaa.xxx.session.repository.SessionRepository; +import de.oaa.xxx.games.bdsm.AufgabeAnzeige; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.BdsmGame; +import de.oaa.xxx.games.bdsm.BdsmGameDurchfuehren; +import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList; +import de.oaa.xxx.games.bdsm.entity.MitspielerEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity; +import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository; +import de.oaa.xxx.games.bdsm.repository.BdsmEinladungRepository; +import de.oaa.xxx.games.bdsm.repository.MitspielerRepository; +import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository; +import de.oaa.xxx.games.history.GameHistoryEntity; +import de.oaa.xxx.games.history.GameHistoryRepository; +import de.oaa.xxx.games.history.GameRole; +import de.oaa.xxx.games.history.GameType; import de.oaa.xxx.user.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,59 +27,69 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import java.security.Principal; +import java.time.Duration; import java.time.LocalDateTime; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.UUID; @RestController -@RequestMapping("/session") +@RequestMapping("/bdsm") @Transactional -public class SessionController { +public class BdsmGameController { - private static final Logger LOGGER = LoggerFactory.getLogger(SessionController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(BdsmGameController.class); - private final SessionRepository sessionRepository; + private final BdsmGameRepository sessionRepository; private final MitspielerRepository mitspielerRepository; private final AktiveSperreRepository aktiveSperreRepository; private final UserRepository userRepository; + private final GameHistoryRepository gameHistoryRepository; + private final BdsmEinladungRepository einladungRepository; private final ObjectMapper objectMapper; - public SessionController(SessionRepository sessionRepository, MitspielerRepository mitspielerRepository, + public BdsmGameController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository, AktiveSperreRepository aktiveSperreRepository, UserRepository userRepository, + GameHistoryRepository gameHistoryRepository, BdsmEinladungRepository einladungRepository, ObjectMapper objectMapper) { this.sessionRepository = sessionRepository; this.mitspielerRepository = mitspielerRepository; this.aktiveSperreRepository = aktiveSperreRepository; this.userRepository = userRepository; + this.gameHistoryRepository = gameHistoryRepository; + this.einladungRepository = einladungRepository; this.objectMapper = objectMapper; } @GetMapping("/{sessionId}") - public ResponseEntity getBySessionId(@PathVariable UUID sessionId) { + public ResponseEntity getBySessionId(@PathVariable UUID sessionId) { return sessionRepository.findById(sessionId) .map(entity -> ResponseEntity.ok(toSession(entity))) .orElse(ResponseEntity.noContent().build()); } @GetMapping - public ResponseEntity getByUserId(@RequestParam UUID userId) { + public ResponseEntity getByUserId(@RequestParam UUID userId) { return sessionRepository.findByUserId(userId) .map(entity -> ResponseEntity.ok(toSession(entity))) .orElse(ResponseEntity.noContent().build()); } @PostMapping - public ResponseEntity create(@RequestBody Session session) { + public ResponseEntity create(@RequestBody BdsmGame session) { String email = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); UUID userId = userRepository.findByEmail(email).map(u -> u.getUserId()).orElse(null); if (userId == null) return ResponseEntity.status(401).build(); - SessionEntity entity = new SessionEntity(); + BdsmGameEntity entity = new BdsmGameEntity(); entity.setSessionId(UUID.randomUUID()); entity.setUserId(userId); entity.setAufgabenAufAktuellemLevel(0); @@ -85,8 +101,16 @@ public class SessionController { entity.setWahrscheinlichkeitStrafe(session.getWahrscheinlichkeitStrafe() != null ? session.getWahrscheinlichkeitStrafe() : 10); entity.setZeitfaktorZeitstrafen(session.getZeitfaktorZeitstrafen() != null ? session.getZeitfaktorZeitstrafen() : 1.0); entity.setLevel(1); + entity.setSetupId(session.getSetupId()); sessionRepository.save(entity); - LOGGER.debug("Session gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]", + // Akzeptierte Einladungen mit der neuen Session verknüpfen + if (session.getSetupId() != null) { + einladungRepository.findBySetupId(session.getSetupId()).stream() + .filter(e -> e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_OWN + || e.getStatus() == BdsmEinladungEntity.Status.ACCEPTED_HOST) + .forEach(e -> e.setSessionId(entity.getSessionId())); + } + LOGGER.debug("BdsmGame gestartet [sessionId={}, userId={}, aufgabenProLevel={}, wahrscheinlichkeitStrafe={}%, wahrscheinlichkeitSperre={}%, zeitfaktorZeitstrafen={}]", entity.getSessionId(), entity.getUserId(), entity.getAufgabenProLevel(), entity.getWahrscheinlichkeitStrafe(), entity.getWahrscheinlichkeitSperre(), entity.getZeitfaktorZeitstrafen()); @@ -96,9 +120,20 @@ public class SessionController { } @DeleteMapping - public ResponseEntity deleteSession(@RequestBody Session session) { + public ResponseEntity deleteSession(@RequestBody BdsmGame session) { return sessionRepository.findById(session.getSessionId()) .map(entity -> { + LocalDateTime endTime = LocalDateTime.now(); + long durationMinutes = Duration.between(entity.getStartZeit(), endTime).toMinutes(); + + GameHistoryEntity entry = new GameHistoryEntity(); + entry.setGameType(GameType.BDSM); + entry.setStartTime(entity.getStartZeit()); + entry.setEndTime(endTime); + entry.setDurationMinutes(durationMinutes); + entry.addParticipant(entity.getUserId(), GameRole.PLAYER); + gameHistoryRepository.save(entry); + aktiveSperreRepository.deleteAll(entity.getAktiveSperren()); mitspielerRepository.deleteAll(entity.getMitspieler()); sessionRepository.delete(entity); @@ -114,7 +149,7 @@ public class SessionController { return ResponseEntity.badRequest().build(); } String aufgaben = objectMapper.writeValueAsString(list); - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) { return ResponseEntity.badRequest().build(); } @@ -130,12 +165,12 @@ public class SessionController { @GetMapping("/{sessionId}/aufgaben/next") public ResponseEntity getNextAufgabe(@PathVariable UUID sessionId) { try { - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) { return ResponseEntity.badRequest().build(); } session.setLetzteAktivitaet(LocalDateTime.now()); - SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session); + BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session); AufgabeAnzeige next = durchfuehren.getNext(); session.setLevel(durchfuehren.getLevel()); session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel()); @@ -165,7 +200,7 @@ public class SessionController { || mitspieler.getVerfuegbareWerkzeuge() == null || mitspieler.getVerfuegbareWerkzeuge().isEmpty()) { return ResponseEntity.badRequest().build(); } - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) { return ResponseEntity.badRequest().build(); } @@ -176,6 +211,8 @@ public class SessionController { entity.setRollen(mitspieler.getRollen()); entity.setSpieltMit(mitspieler.getSpieltMit()); entity.setWerkzeuge(mitspieler.getVerfuegbareWerkzeuge()); + entity.setUserId(mitspieler.getUserId()); + entity.setEigenesGeraet(mitspieler.isEigenesGeraet()); entity.setSession(session); mitspielerRepository.save(entity); return ResponseEntity.accepted().build(); @@ -184,9 +221,9 @@ public class SessionController { @GetMapping("/{sessionId}/finisher") public ResponseEntity> getFinisher(@PathVariable UUID sessionId) { try { - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) return ResponseEntity.badRequest().build(); - SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session); + BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session); return ResponseEntity.ok(durchfuehren.getFinisher()); } catch (Exception exception) { LOGGER.error(exception.getMessage(), exception); @@ -197,9 +234,9 @@ public class SessionController { @PostMapping("/{sessionId}/backToLevel5") public ResponseEntity backToLevel5(@PathVariable UUID sessionId) { try { - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) return ResponseEntity.badRequest().build(); - SessionDurchfuehren durchfuehren = new SessionDurchfuehren(session); + BdsmGameDurchfuehren durchfuehren = new BdsmGameDurchfuehren(session); durchfuehren.backToLvl5(); session.setLevel(durchfuehren.getLevel()); session.setAufgabenAufAktuellemLevel(durchfuehren.getAufgabenAufAktuellemLevel()); @@ -211,8 +248,62 @@ public class SessionController { } } - private Session toSession(SessionEntity entity) { - Session session = new Session(); + @GetMapping("/{sessionId}/mitspieler/me") + public ResponseEntity> getMeinMitspieler(@PathVariable UUID sessionId, Principal principal) { + UUID userId = userRepository.findByEmail(principal.getName()).map(u -> u.getUserId()).orElse(null); + if (userId == null) return ResponseEntity.status(401).build(); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); + if (session == null) return ResponseEntity.notFound().build(); + return session.getMitspieler().stream() + .filter(m -> userId.equals(m.getUserId())) + .findFirst() + .map(m -> { + Map result = new LinkedHashMap<>(); + result.put("mitspielerId", m.getMitspielerId()); + result.put("name", m.getName()); + result.put("eigenesGeraet", m.isEigenesGeraet()); + return ResponseEntity.ok(result); + }) + .orElse(ResponseEntity.noContent().build()); + } + + record ActiveTaskRequest(String taskJson, LocalDateTime timerStartedAt) {} + record ActiveTaskResponse(String taskJson, Long elapsedSeconds) {} + + @PutMapping("/{sessionId}/active-task") + public ResponseEntity setActiveTask(@PathVariable UUID sessionId, @RequestBody ActiveTaskRequest req) { + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); + if (session == null) return ResponseEntity.notFound().build(); + session.setActiveTaskJson(req.taskJson()); + session.setTaskStartedAt(req.timerStartedAt()); + sessionRepository.save(session); + return ResponseEntity.accepted().build(); + } + + @DeleteMapping("/{sessionId}/active-task") + public ResponseEntity clearActiveTask(@PathVariable UUID sessionId) { + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); + if (session == null) return ResponseEntity.notFound().build(); + session.setActiveTaskJson(null); + session.setTaskStartedAt(null); + sessionRepository.save(session); + return ResponseEntity.accepted().build(); + } + + @GetMapping("/{sessionId}/active-task") + public ResponseEntity getActiveTask(@PathVariable UUID sessionId) { + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); + if (session == null) return ResponseEntity.notFound().build(); + if (session.getActiveTaskJson() == null) return ResponseEntity.noContent().build(); + Long elapsed = null; + if (session.getTaskStartedAt() != null) { + elapsed = Duration.between(session.getTaskStartedAt(), LocalDateTime.now()).getSeconds(); + } + return ResponseEntity.ok(new ActiveTaskResponse(session.getActiveTaskJson(), elapsed)); + } + + private BdsmGame toSession(BdsmGameEntity entity) { + BdsmGame session = new BdsmGame(); session.setSessionId(entity.getSessionId()); session.setUserId(entity.getUserId()); session.setAufgabenProLevel(entity.getAufgabenProLevel()); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/controller/SperreController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/SperreController.java similarity index 81% rename from xxxthegame/src/main/java/de/oaa/xxx/session/controller/SperreController.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/SperreController.java index 36372e8..e31022e 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/controller/SperreController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/controller/SperreController.java @@ -1,15 +1,15 @@ -package de.oaa.xxx.session.controller; +package de.oaa.xxx.games.bdsm.controller; -import de.oaa.xxx.session.AktiveSperre; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.entity.AktiveSperreEntity; -import de.oaa.xxx.session.entity.SessionEntity; -import de.oaa.xxx.session.repository.AktiveSperreRepository; -import de.oaa.xxx.session.repository.MitspielerRepository; -import de.oaa.xxx.session.repository.SessionRepository; -import de.oaa.xxx.session.sperre.SperreCallback; -import de.oaa.xxx.session.sperre.SperreVerarbeiten; -import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback; +import de.oaa.xxx.games.bdsm.AktiveSperre; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity; +import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository; +import de.oaa.xxx.games.bdsm.repository.MitspielerRepository; +import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository; +import de.oaa.xxx.games.bdsm.sperre.SperreCallback; +import de.oaa.xxx.games.bdsm.sperre.SperreVerarbeiten; +import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; @@ -26,18 +26,18 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; -@RestController("sessionSperreController") -@RequestMapping("/session/sperre") +@RestController("bdsmSperreController") +@RequestMapping("/bdsm/sperre") @Transactional public class SperreController { private static final Logger LOGGER = LoggerFactory.getLogger(SperreController.class); - private final SessionRepository sessionRepository; + private final BdsmGameRepository sessionRepository; private final MitspielerRepository mitspielerRepository; private final AktiveSperreRepository aktiveSperreRepository; - public SperreController(SessionRepository sessionRepository, MitspielerRepository mitspielerRepository, + public SperreController(BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository, AktiveSperreRepository aktiveSperreRepository) { this.sessionRepository = sessionRepository; this.mitspielerRepository = mitspielerRepository; @@ -78,7 +78,7 @@ public class SperreController { @GetMapping("/aktive") public ResponseEntity> getAktiveSperren(@RequestParam UUID sessionId) { try { - SessionEntity session = sessionRepository.findById(sessionId).orElse(null); + BdsmGameEntity session = sessionRepository.findById(sessionId).orElse(null); if (session == null) return ResponseEntity.noContent().build(); List mitspielerList = session.getMitspieler().stream() .map(m -> m.toMitspieler()) diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/AktiveSperreEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/AktiveSperreEntity.java similarity index 89% rename from xxxthegame/src/main/java/de/oaa/xxx/session/entity/AktiveSperreEntity.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/AktiveSperreEntity.java index 83a806b..5365ba5 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/AktiveSperreEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/AktiveSperreEntity.java @@ -1,8 +1,8 @@ -package de.oaa.xxx.session.entity; +package de.oaa.xxx.games.bdsm.entity; -import de.oaa.xxx.session.AktiveSperre; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.Werkzeug; +import de.oaa.xxx.games.bdsm.AktiveSperre; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.Werkzeug; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; @@ -50,7 +50,7 @@ public class AktiveSperreEntity { private String releaseText; @ManyToOne @JoinColumn(name = "sessionId", nullable = false) - private SessionEntity session; + private BdsmGameEntity session; public AktiveSperre toSperre(List mitspielerList) { AktiveSperre sperre = new AktiveSperre(); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmDefaultsEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmDefaultsEntity.java new file mode 100644 index 0000000..45444c0 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmDefaultsEntity.java @@ -0,0 +1,30 @@ +package de.oaa.xxx.games.bdsm.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "bdsm_defaults") +public class BdsmDefaultsEntity { + + @Id + @Column(name = "user_id") + private UUID userId; + + @Column(length = 100) + private String spieltMit; + + @Column(length = 200) + private String rollen; + + @Column(length = 200) + private String werkzeuge; +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmEinladungEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmEinladungEntity.java new file mode 100644 index 0000000..9fe0ea8 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmEinladungEntity.java @@ -0,0 +1,50 @@ +package de.oaa.xxx.games.bdsm.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "bdsm_einladung") +public class BdsmEinladungEntity { + + public enum Status { + PENDING, ACCEPTED_OWN, ACCEPTED_HOST, DECLINED, CANCELLED + } + + @Id + @Column + private UUID einladungId; + + @Column(nullable = false) + private UUID setupId; + + @Column + private UUID sessionId; + + @Column(nullable = false) + private UUID inviterId; + + @Column(nullable = false) + private UUID inviteeId; + + @Column(nullable = false) + private int slotIndex; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + private Status status; + + @Column(nullable = false) + private LocalDateTime createdAt; +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/SessionEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmGameEntity.java similarity index 81% rename from xxxthegame/src/main/java/de/oaa/xxx/session/entity/SessionEntity.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmGameEntity.java index 0015ae5..0a987bc 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/SessionEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/BdsmGameEntity.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.session.entity; +package de.oaa.xxx.games.bdsm.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -18,7 +18,7 @@ import java.util.UUID; @Setter @Entity @Table(name = "session") -public class SessionEntity { +public class BdsmGameEntity { @Id @Column @@ -47,10 +47,16 @@ public class SessionEntity { private String aufgaben; @Column private Double zeitfaktorZeitstrafen; + @Column(columnDefinition = "TEXT") + private String activeTaskJson; + @Column + private LocalDateTime taskStartedAt; + @Column + private UUID setupId; @Override public String toString() { - return "SessionEntity[sessionId=" + sessionId + ", userId=" + userId + return "BdsmGameEntity[sessionId=" + sessionId + ", userId=" + userId + ", level=" + level + ", aufgaben=" + aufgabenAufAktuellemLevel + "/" + aufgabenProLevel + ", pStrafe=" + wahrscheinlichkeitStrafe + "%, pSperre=" + wahrscheinlichkeitSperre + "%" + ", zeitfaktor=" + zeitfaktorZeitstrafen + ", start=" + startZeit + "]"; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/MitspielerEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/MitspielerEntity.java similarity index 86% rename from xxxthegame/src/main/java/de/oaa/xxx/session/entity/MitspielerEntity.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/MitspielerEntity.java index 040fced..49755e7 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/entity/MitspielerEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/entity/MitspielerEntity.java @@ -1,77 +1,83 @@ -package de.oaa.xxx.session.entity; - -import de.oaa.xxx.session.GeschlechtEnum; -import de.oaa.xxx.session.Mitspieler; -import de.oaa.xxx.session.RolleEnum; -import de.oaa.xxx.session.Werkzeug; -import jakarta.persistence.CollectionTable; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -@Getter -@Setter -@Entity -@Table(name = "mitspieler") -public class MitspielerEntity { - - @Id - @Column - private UUID mitspielerId; - @Column - private String name; - @Enumerated(EnumType.STRING) - @Column - private GeschlechtEnum geschlecht; - @Enumerated(EnumType.STRING) - @ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER) - @CollectionTable(name = "mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId")) - @Column(name = "werkzeug") - private List werkzeuge = new ArrayList<>(); - @Enumerated(EnumType.STRING) - @ElementCollection(targetClass = GeschlechtEnum.class, fetch = FetchType.EAGER) - @CollectionTable(name = "mitspieler_spieltMit", joinColumns = @JoinColumn(name = "mitspielerId")) - @Column(name = "geschlecht") - private List spieltMit = new ArrayList<>(); - @Enumerated(EnumType.STRING) - @ElementCollection(targetClass = RolleEnum.class, fetch = FetchType.EAGER) - @CollectionTable(name = "mitspieler_rollen", joinColumns = @JoinColumn(name = "mitspielerId")) - @Column(name = "rolle") - private List rollen = new ArrayList<>(); - @ManyToOne - @JoinColumn(name = "sessionId", nullable = false) - private SessionEntity session; - @OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER) - private List aktiveSperren = new ArrayList<>(); - - @Override - public String toString() { - return "MitspielerEntity[mitspielerId=" + mitspielerId + ", name=" + name - + ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]"; - } - - public Mitspieler toMitspieler() { - Mitspieler mitspieler = new Mitspieler(); - mitspieler.setGeschlecht(geschlecht); - mitspieler.setId(mitspielerId); - mitspieler.setName(name); - mitspieler.setRollen(rollen); - mitspieler.setSpieltMit(spieltMit); - mitspieler.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge)); - return mitspieler; - } -} +package de.oaa.xxx.games.bdsm.entity; + +import de.oaa.xxx.games.bdsm.GeschlechtEnum; +import de.oaa.xxx.games.bdsm.Mitspieler; +import de.oaa.xxx.games.bdsm.RolleEnum; +import de.oaa.xxx.games.bdsm.Werkzeug; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "mitspieler") +public class MitspielerEntity { + + @Id + @Column + private UUID mitspielerId; + @Column + private UUID userId; + @Column + private boolean eigenesGeraet; + @Column + private String name; + @Enumerated(EnumType.STRING) + @Column + private GeschlechtEnum geschlecht; + @Enumerated(EnumType.STRING) + @ElementCollection(targetClass = Werkzeug.class, fetch = FetchType.EAGER) + @CollectionTable(name = "mitspieler_werkzeuge", joinColumns = @JoinColumn(name = "mitspielerId")) + @Column(name = "werkzeug") + private List werkzeuge = new ArrayList<>(); + @Enumerated(EnumType.STRING) + @ElementCollection(targetClass = GeschlechtEnum.class, fetch = FetchType.EAGER) + @CollectionTable(name = "mitspieler_spieltMit", joinColumns = @JoinColumn(name = "mitspielerId")) + @Column(name = "geschlecht") + private List spieltMit = new ArrayList<>(); + @Enumerated(EnumType.STRING) + @ElementCollection(targetClass = RolleEnum.class, fetch = FetchType.EAGER) + @CollectionTable(name = "mitspieler_rollen", joinColumns = @JoinColumn(name = "mitspielerId")) + @Column(name = "rolle") + private List rollen = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "sessionId", nullable = false) + private BdsmGameEntity session; + @OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER) + private List aktiveSperren = new ArrayList<>(); + + @Override + public String toString() { + return "MitspielerEntity[mitspielerId=" + mitspielerId + ", name=" + name + + ", geschlecht=" + geschlecht + ", rollen=" + rollen + ", werkzeuge=" + werkzeuge + "]"; + } + + public Mitspieler toMitspieler() { + Mitspieler mitspieler = new Mitspieler(); + mitspieler.setGeschlecht(geschlecht); + mitspieler.setId(mitspielerId); + mitspieler.setUserId(userId); + mitspieler.setEigenesGeraet(eigenesGeraet); + mitspieler.setName(name); + mitspieler.setRollen(rollen); + mitspieler.setSpieltMit(spieltMit); + mitspieler.setVerfuegbareWerkzeuge(new ArrayList<>(werkzeuge)); + return mitspieler; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/AktiveSperreRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/AktiveSperreRepository.java similarity index 86% rename from xxxthegame/src/main/java/de/oaa/xxx/session/repository/AktiveSperreRepository.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/AktiveSperreRepository.java index 549bcd6..acf8fa4 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/AktiveSperreRepository.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/AktiveSperreRepository.java @@ -1,6 +1,6 @@ -package de.oaa.xxx.session.repository; +package de.oaa.xxx.games.bdsm.repository; -import de.oaa.xxx.session.entity.AktiveSperreEntity; +import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmDefaultsRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmDefaultsRepository.java new file mode 100644 index 0000000..c144a2d --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmDefaultsRepository.java @@ -0,0 +1,11 @@ +package de.oaa.xxx.games.bdsm.repository; + +import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface BdsmDefaultsRepository extends JpaRepository { + Optional findByUserId(UUID userId); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmEinladungRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmEinladungRepository.java new file mode 100644 index 0000000..4ac66bc --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmEinladungRepository.java @@ -0,0 +1,17 @@ +package de.oaa.xxx.games.bdsm.repository; + +import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmEinladungEntity.Status; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.UUID; + +public interface BdsmEinladungRepository extends JpaRepository { + + List findBySetupId(UUID setupId); + + List findByInviteeIdAndStatus(UUID inviteeId, Status status); + + List findByInviterIdAndStatus(UUID inviterId, Status status); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmGameRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmGameRepository.java new file mode 100644 index 0000000..d3aedeb --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/BdsmGameRepository.java @@ -0,0 +1,12 @@ +package de.oaa.xxx.games.bdsm.repository; + +import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface BdsmGameRepository extends JpaRepository { + + Optional findByUserId(UUID userId); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/MitspielerRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/MitspielerRepository.java similarity index 62% rename from xxxthegame/src/main/java/de/oaa/xxx/session/repository/MitspielerRepository.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/MitspielerRepository.java index 07dece5..f920772 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/MitspielerRepository.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/repository/MitspielerRepository.java @@ -1,6 +1,6 @@ -package de.oaa.xxx.session.repository; +package de.oaa.xxx.games.bdsm.repository; -import de.oaa.xxx.session.entity.MitspielerEntity; +import de.oaa.xxx.games.bdsm.entity.MitspielerEntity; import org.springframework.data.jpa.repository.JpaRepository; import java.util.UUID; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreCallback.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreCallback.java similarity index 87% rename from xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreCallback.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreCallback.java index 2633076..83f5dc8 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreCallback.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreCallback.java @@ -1,6 +1,6 @@ -package de.oaa.xxx.session.sperre; +package de.oaa.xxx.games.bdsm.sperre; -import de.oaa.xxx.session.Callback; +import de.oaa.xxx.games.bdsm.Callback; import java.util.UUID; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreVerarbeiten.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreVerarbeiten.java similarity index 77% rename from xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreVerarbeiten.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreVerarbeiten.java index 7796687..051d434 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperreVerarbeiten.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperreVerarbeiten.java @@ -1,14 +1,14 @@ -package de.oaa.xxx.session.sperre; +package de.oaa.xxx.games.bdsm.sperre; import com.fasterxml.jackson.databind.ObjectMapper; -import de.oaa.xxx.session.aufgaben.AufgabenList; -import de.oaa.xxx.session.aufgaben.Sperre; -import de.oaa.xxx.session.entity.AktiveSperreEntity; -import de.oaa.xxx.session.entity.MitspielerEntity; -import de.oaa.xxx.session.entity.SessionEntity; -import de.oaa.xxx.session.repository.AktiveSperreRepository; -import de.oaa.xxx.session.repository.MitspielerRepository; -import de.oaa.xxx.session.repository.SessionRepository; +import de.oaa.xxx.games.bdsm.aufgaben.AufgabenList; +import de.oaa.xxx.games.bdsm.aufgaben.Sperre; +import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity; +import de.oaa.xxx.games.bdsm.entity.MitspielerEntity; +import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity; +import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository; +import de.oaa.xxx.games.bdsm.repository.MitspielerRepository; +import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository; import java.time.LocalDateTime; import java.util.Optional; @@ -19,9 +19,9 @@ public class SperreVerarbeiten { private final ObjectMapper objectMapper = new ObjectMapper(); - public void sperreAnwenden(SperreCallback callback, SessionRepository sessionRepository, + public void sperreAnwenden(SperreCallback callback, BdsmGameRepository sessionRepository, MitspielerRepository mitspielerRepository, AktiveSperreRepository sperreRepository) throws Exception { - SessionEntity session = sessionRepository.findById(callback.getSessionId()).orElse(null); + BdsmGameEntity session = sessionRepository.findById(callback.getSessionId()).orElse(null); MitspielerEntity mitspieler = mitspielerRepository.findById(callback.getSpielerId()).orElse(null); if (session != null) { AufgabenList aufgaben = objectMapper.readValue(session.getAufgaben(), AufgabenList.class); @@ -56,7 +56,7 @@ public class SperreVerarbeiten { sperreRepository.save(verlaengern); } - private void fill(SperreCallback callback, SessionEntity session, MitspielerEntity mitspieler, + private void fill(SperreCallback callback, BdsmGameEntity session, MitspielerEntity mitspieler, Sperre sperre, AktiveSperreEntity aktiv) { aktiv.setAktiveSperreId(UUID.randomUUID()); LocalDateTime now = LocalDateTime.now(); @@ -70,7 +70,7 @@ public class SperreVerarbeiten { aktiv.setReleaseText(callback.getReleaseText()); } - private Integer berechneDauer(SessionEntity session, Sperre sperre) { + private Integer berechneDauer(BdsmGameEntity session, Sperre sperre) { Integer minuten = 30; if (sperre.getMinutenVon() != null) { if (sperre.getMinutenBis() != null) { diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperrenVerlaengernCallback.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperrenVerlaengernCallback.java similarity index 85% rename from xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperrenVerlaengernCallback.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperrenVerlaengernCallback.java index 58bf7fa..3d26541 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/sperre/SperrenVerlaengernCallback.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/sperre/SperrenVerlaengernCallback.java @@ -1,6 +1,6 @@ -package de.oaa.xxx.session.sperre; +package de.oaa.xxx.games.bdsm.sperre; -import de.oaa.xxx.session.Callback; +import de.oaa.xxx.games.bdsm.Callback; import java.util.UUID; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/LockeeInvitationController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/LockeeInvitationController.java index ee87f07..a59a830 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/LockeeInvitationController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/LockeeInvitationController.java @@ -22,9 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import de.oaa.xxx.games.chastity.cardlock.CardlockRepository; -import de.oaa.xxx.social.SseService; -import de.oaa.xxx.social.entity.MessageEntity; -import de.oaa.xxx.social.repository.MessageRepository; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; @RestController @@ -34,8 +32,7 @@ public class LockeeInvitationController { private final LockeeInvitationRepository lockeeInvitationRepository; private final CardlockRepository cardlockRepository; private final UserRepository userRepository; - private final MessageRepository messageRepository; - private final SseService sseService; + private final SystemMessageService systemMessageService; @Value("${app.base-url:http://localhost:8080}") private String baseUrl; @@ -45,27 +42,15 @@ public class LockeeInvitationController { public LockeeInvitationController(LockeeInvitationRepository lockeeInvitationRepository, CardlockRepository cardlockRepository, UserRepository userRepository, - MessageRepository messageRepository, - SseService sseService) { + SystemMessageService systemMessageService) { this.lockeeInvitationRepository = lockeeInvitationRepository; this.cardlockRepository = cardlockRepository; this.userRepository = userRepository; - this.messageRepository = messageRepository; - this.sseService = sseService; + this.systemMessageService = systemMessageService; } - private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl) { - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(senderId); - msg.setReceiverId(receiverId); - msg.setText(text); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - if (targetUrl != null) msg.setTargetUrl(targetUrl); - messageRepository.save(msg); - long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true); - sseService.push(receiverId, "NOTIFICATION", java.util.Map.of("unreadCount", unread, "text", text)); + private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) { + systemMessageService.send(senderId, receiverId, text, targetUrl, cause); } private String generateUnlockCode(int lines) { @@ -154,7 +139,7 @@ public class LockeeInvitationController { String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; sendMessage(myId, inv.getLockeeUserId(), me.getName() + " hat die Lockee-Einladung für das Lock „" + lockName + "\" zurückgezogen.", - null); + null, de.oaa.xxx.social.entity.MessageCause.INVITATION); } return ResponseEntity.noContent().build(); @@ -248,7 +233,7 @@ public class LockeeInvitationController { String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; sendMessage(myId, inv.getKeyholderUserId(), me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" angenommen.", - "/keyholder.html"); + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.INVITATION); return ResponseEntity.ok(Map.of( "lockId", lock.getLockId().toString(), @@ -278,7 +263,7 @@ public class LockeeInvitationController { String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; sendMessage(myId, inv.getKeyholderUserId(), me.getName() + " hat die Einladung als Lockee für das Lock „" + lockName + "\" abgelehnt.", - null); + null, de.oaa.xxx.social.entity.MessageCause.INVITATION); } return ResponseEntity.noContent().build(); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java index 05f8d11..72a49f1 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java @@ -41,7 +41,7 @@ import de.oaa.xxx.games.chastity.KeyholderInvitationEntity; import de.oaa.xxx.games.chastity.KeyholderInvitationRepository; import de.oaa.xxx.games.chastity.LockeeInvitationEntity; import de.oaa.xxx.games.chastity.LockeeInvitationRepository; -import de.oaa.xxx.games.chastity.history.LockHistoryRepository; +import de.oaa.xxx.games.history.GameHistoryRepository; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.Task; @@ -49,9 +49,7 @@ import de.oaa.xxx.games.chastity.verification.VerificationEntity; import de.oaa.xxx.games.chastity.verification.VerificationRepository; import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity; import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; -import de.oaa.xxx.social.SseService; -import de.oaa.xxx.social.entity.MessageEntity; -import de.oaa.xxx.social.repository.MessageRepository; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; @RestController @@ -65,15 +63,14 @@ public class CardLockController { private final VerificationRepository verificationRepository; private final VerificationVoteRepository verificationVoteRepository; private final HygieneViolationRepository hygieneViolationRepository; - private final MessageRepository messageRepository; private final LockeeInvitationRepository lockeeInvitationRepository; private final AssignedTaskRepository assignedTaskRepository; private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; private final CommunityTaskVoteRepository communityTaskVoteRepository; private final UnlockCodeHistoryRepository unlockCodeHistoryRepository; private final UnlockCodeHistoryService unlockCodeHistoryService; - private final LockHistoryRepository lockHistoryRepository; - private final SseService sseService; + private final GameHistoryRepository gameHistoryRepository; + private final SystemMessageService systemMessageService; @Value("${app.base-url:http://localhost:8080}") private String baseUrl; @@ -85,15 +82,14 @@ public class CardLockController { VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, HygieneViolationRepository hygieneViolationRepository, - MessageRepository messageRepository, LockeeInvitationRepository lockeeInvitationRepository, AssignedTaskRepository assignedTaskRepository, KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, CommunityTaskVoteRepository communityTaskVoteRepository, UnlockCodeHistoryRepository unlockCodeHistoryRepository, UnlockCodeHistoryService unlockCodeHistoryService, - LockHistoryRepository lockHistoryRepository, - SseService sseService) { + GameHistoryRepository gameHistoryRepository, + SystemMessageService systemMessageService) { this.cardlockRepository = cardlockRepository; this.cardLockRepository = cardLockRepository; this.userRepository = userRepository; @@ -101,15 +97,14 @@ public class CardLockController { this.verificationRepository = verificationRepository; this.verificationVoteRepository = verificationVoteRepository; this.hygieneViolationRepository = hygieneViolationRepository; - this.messageRepository = messageRepository; this.lockeeInvitationRepository = lockeeInvitationRepository; this.assignedTaskRepository = assignedTaskRepository; this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; this.communityTaskVoteRepository = communityTaskVoteRepository; this.unlockCodeHistoryRepository = unlockCodeHistoryRepository; this.unlockCodeHistoryService = unlockCodeHistoryService; - this.lockHistoryRepository = lockHistoryRepository; - this.sseService = sseService; + this.gameHistoryRepository = gameHistoryRepository; + this.systemMessageService = systemMessageService; } record CreateCardLockRequest( @@ -194,7 +189,7 @@ public class CardLockController { String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock"; sendMessage(myId, lockee.getUserId(), me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.", - "/einladungen.html"); + "/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION); return ResponseEntity.ok(Map.of( "lockId", lock.getLockId().toString(), @@ -257,7 +252,7 @@ public class CardLockController { String lockName = req.name() != null && !req.name().isBlank() ? req.name() : "Unbenanntes Lock"; sendMessage(me.getUserId(), kh.getUserId(), me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.", - "/einladungen.html"); + "/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION); keyholderPending = true; } @@ -282,7 +277,7 @@ public class CardLockController { var l = lockOpt.get(); if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); - CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository); + CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository); CardDTO dto = service.getNextCard(); if (dto == null) return ResponseEntity.status(409).body(Map.of("error", "Keine Karte verfügbar")); @@ -300,7 +295,7 @@ public class CardLockController { userRepository.findById(l.getKeyholder()).ifPresent(kh -> sendMessage(l.getLockee(), kh.getUserId(), "Deine Lockee hat eine Aufgaben-Karte gezogen – wähle eine Aufgabe aus.", - "/keyholder.html")); + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE)); taskPending = "KEYHOLDER"; } else if ("COMMUNITY".equals(l.getTaskCardMode())) { @@ -320,9 +315,14 @@ public class CardLockController { result.put("unlockCode", dto.unlockCode() != null ? dto.unlockCode() : ""); if (taskPending != null) result.put("taskPending", taskPending); - // Grüne Karte → Entsperrcode-Historie speichern + // Grüne Karte → Entsperrcode-Historie speichern + Keyholder benachrichtigen if (dto.unlockCode() != null && !dto.unlockCode().isBlank()) { unlockCodeHistoryService.save(myId, l.getLockId(), l.getName(), dto.unlockCode(), "GREEN_CARD"); + if (l.getKeyholder() != null) { + sendMessage(myId, l.getKeyholder(), + meOpt.get().getName() + " hat die grüne Karte gezogen! Der Entsperrcode wurde angezeigt.", + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); + } } return ResponseEntity.ok(result); @@ -417,7 +417,7 @@ public class CardLockController { var l = lockOpt.get(); if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); - CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository); + CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository); service.clearTask(); return ResponseEntity.noContent().build(); } @@ -434,8 +434,16 @@ public class CardLockController { var l = lockOpt.get(); if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); - CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository); + CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository); service.putBackGreen(); + + // Grüne Karte zurückgelegt → Keyholder benachrichtigen + if (l.getKeyholder() != null) { + sendMessage(myId, l.getKeyholder(), + meOpt.get().getName() + " hat die grüne Karte zurückgelegt und bleibt im Lock.", + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); + } + return ResponseEntity.noContent().build(); } @@ -557,7 +565,7 @@ public class CardLockController { lockDirty = true; sendMessage(l.getKeyholder(), l.getLockee(), "Die dir gestellte Aufgabe ist abgelaufen, ohne dass du reagiert hast. Die Strafe wurde automatisch angewendet.", - "/activelock.html?lockId=" + l.getLockId()); + "/activelock.html?lockId=" + l.getLockId(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE); } if (lockDirty) cardlockRepository.save(l); @@ -695,7 +703,7 @@ public class CardLockController { var lockee = meOpt.get(); sendMessage(myId, lock.getKeyholder(), "📸 " + lockee.getName() + " hat eine Verifikation eingereicht.", - "/keyholder.html"); + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); } return ResponseEntity.noContent().build(); @@ -793,14 +801,9 @@ public class CardLockController { if (lockOpt.isPresent()) { var lock = lockOpt.get(); String lockName = lock.getName() != null && !lock.getName().isBlank() ? lock.getName() : "Unbenanntes Lock"; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(myId); - msg.setReceiverId(lock.getLockee()); - msg.setText(me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt."); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - messageRepository.save(msg); + sendMessage(myId, lock.getLockee(), + me.getName() + " hat die Einladung als Keyholder*In für das Lock „" + lockName + "\" abgelehnt.", + null, de.oaa.xxx.social.entity.MessageCause.INVITATION); } return ResponseEntity.noContent().build(); @@ -855,14 +858,9 @@ public class CardLockController { String lockName = lockOpt.get().getName() != null && !lockOpt.get().getName().isBlank() ? lockOpt.get().getName() : "Unbenanntes Lock"; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(myId); - msg.setReceiverId(inv.getKeyholderUserId()); - msg.setText(me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen."); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - messageRepository.save(msg); + sendMessage(myId, inv.getKeyholderUserId(), + me.getName() + " hat die Keyholder-Einladung für das Lock „" + lockName + "\" zurückgezogen.", + null, de.oaa.xxx.social.entity.MessageCause.INVITATION); return ResponseEntity.noContent().build(); } @@ -1069,7 +1067,7 @@ public class CardLockController { if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); // Entsperrung protokollieren (History + XP) – gültig nur wenn Keyholder vorhanden und kein Auto-Notfall - CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, lockHistoryRepository, userRepository); + CardLockService service = new CardLockService(l, verificationRepository, verificationVoteRepository, cardLockRepository, gameHistoryRepository, userRepository); service.unlock(l.getUnlockCode()); var verifications = verificationRepository.findByLockId(lockId); @@ -1124,14 +1122,8 @@ public class CardLockController { ? me.getName() + " hat " + toAdd.size() + " Karte(n) zu deinem Lock hinzugefügt: " + detail + "." : me.getName() + " hat Karten zu deinem Lock hinzugefügt."; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(myId); - msg.setReceiverId(l.getLockee()); - msg.setText(msgText); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - messageRepository.save(msg); + sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId, + de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1189,14 +1181,8 @@ public class CardLockController { ? me.getName() + " hat " + removed.size() + " Karte(n) aus deinem Lock entfernt: " + detail + "." : me.getName() + " hat Karten aus deinem Lock entfernt."; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(myId); - msg.setReceiverId(l.getLockee()); - msg.setText(msgText); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - messageRepository.save(msg); + sendMessage(myId, l.getLockee(), msgText, "/activelock.html?lockId=" + lockId, + de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1221,19 +1207,8 @@ public class CardLockController { } } - private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl) { - if (senderId == null || receiverId == null) return; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(senderId); - msg.setReceiverId(receiverId); - msg.setText(text); - msg.setSentAt(LocalDateTime.now()); - msg.setSystemMessage(true); - if (targetUrl != null) msg.setTargetUrl(targetUrl); - messageRepository.save(msg); - long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true); - sseService.push(receiverId, "NOTIFICATION", java.util.Map.of("unreadCount", unread, "text", text)); + private void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, de.oaa.xxx.social.entity.MessageCause cause) { + systemMessageService.send(senderId, receiverId, text, targetUrl, cause); } @GetMapping("/cardlock/unlock-history") @@ -1305,7 +1280,7 @@ public class CardLockController { sendMessage(me.getUserId(), l.getLockee(), me.getName() + " hat dir eine Aufgabe gestellt. Du hast " + req.acceptDeadlineMinutes() + " Minuten, um sie anzunehmen.", - "/activelock.html?lockId=" + lockId); + "/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1365,7 +1340,7 @@ public class CardLockController { assignedTaskRepository.save(task); cardlockRepository.save(l); - sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe angenommen.", "/keyholder.html"); + sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe angenommen.", "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1397,7 +1372,7 @@ public class CardLockController { assignedTaskRepository.save(task); cardlockRepository.save(l); - sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", "/keyholder.html"); + sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1465,7 +1440,7 @@ public class CardLockController { until.toLocalDate().format(java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")) + " " + until.toLocalTime().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm")) + " Uhr eingefroren.", - "/activelock.html?lockId=" + lockId); + "/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1490,7 +1465,7 @@ public class CardLockController { cardlockRepository.save(l); sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.", - "/activelock.html?lockId=" + lockId); + "/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1512,7 +1487,7 @@ public class CardLockController { sendMessage(myId, l.getLockee(), "Dein Keyholder hat das Lock freigeschaltet. Du erhältst beim nächsten Laden deinen Entsperrcode.", - "/activelock.html?lockId=" + lockId); + "/activelock.html?lockId=" + lockId, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); return ResponseEntity.noContent().build(); } @@ -1542,7 +1517,7 @@ public class CardLockController { // Keyholderin benachrichtigen sendMessage(myId, l.getKeyholder(), "⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde, sonst öffnet sich das Lock automatisch.", - "/keyholder.html"); + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY); } cardlockRepository.save(l); return ResponseEntity.noContent().build(); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java index 39b49f7..9ab95fb 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java @@ -11,10 +11,9 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.oaa.xxx.games.chastity.LockType; import de.oaa.xxx.games.chastity.ProcessLock; -import de.oaa.xxx.games.chastity.history.LockHistoryEntity; -import de.oaa.xxx.games.chastity.history.LockHistoryRepository; +import de.oaa.xxx.games.history.GameHistoryEntity; +import de.oaa.xxx.games.history.GameHistoryRepository; import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.verification.VerificationEntity; import de.oaa.xxx.games.chastity.verification.VerificationRepository; @@ -29,15 +28,15 @@ public class CardLockService extends ProcessLock { private VerificationRepository verificationRepository; private VerificationVoteRepository verificationVoteRepository; private CardLockRepository cardLockRepository; - private LockHistoryRepository lockHistoryRepository; + private GameHistoryRepository gameHistoryRepository; private UserRepository userRepository; - public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, LockHistoryRepository lockHistoryRepository, UserRepository userRepository) { + public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, GameHistoryRepository gameHistoryRepository, UserRepository userRepository) { this.lock = lock; this.verificationRepository = verificationRepository; this.verificationVoteRepository = verificationVoteRepository; this.cardLockRepository = cardLockRepository; - this.lockHistoryRepository = lockHistoryRepository; + this.gameHistoryRepository = gameHistoryRepository; this.userRepository = userRepository; } @@ -101,28 +100,30 @@ public class CardLockService extends ProcessLock { public void unlock(String unlockCode) { this.lock.setUnlockTime(LocalDateTime.now()); - // Self-Lock oder automatische Entsperrung ohne Keyholder-Zustimmung → ungültig - boolean valid = lock.getKeyholder() != null && !lock.isEmergencyAutoUnlocked(); - if (!this.lock.isTestLock()) { - if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { - Set verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream() - .filter(verification -> isValid(verification)) - .map(verification -> verification.getVerificationTime().toLocalDate()) - .collect(Collectors.toSet()); - - LocalDate current = this.lock.getStartTime().toLocalDate(); - LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1); - - while (!current.isAfter(last)) { - if (!verifications.contains(current)) { - valid = false; - break; - } - current = current.plusDays(1); + boolean valid = true; + if (lock.isEmergencyAutoUnlocked()) { + valid = false; + LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); + } + if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + Set verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream() + .filter(verification -> isValid(verification)) + .map(verification -> verification.getVerificationTime().toLocalDate()) + .collect(Collectors.toSet()); + + LocalDate current = this.lock.getStartTime().toLocalDate(); + LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1); + + while (!current.isAfter(last)) { + if (!verifications.contains(current)) { + valid = false; + LOGGER.debug("Lock invalid - no daily verification on %s", current.toString()); + break; } + current = current.plusDays(1); } - - } lock.setUnlockTime(LocalDateTime.now()); @@ -132,31 +133,18 @@ public class CardLockService extends ProcessLock { if (valid) { long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes(); - // Eintrag für den Lockee - LockHistoryEntity lockeeEntry = new LockHistoryEntity(); - lockeeEntry.setUserId(lock.getLockee()); - lockeeEntry.setLockedBy(lock.getKeyholder()); - lockeeEntry.setLockName(lock.getName()); - lockeeEntry.setStartTime(lock.getStartTime()); - lockeeEntry.setEndTime(lock.getUnlockTime()); - lockeeEntry.setType(LockType.CARD); - lockeeEntry.setDurationMinutes(durationMinutes); - lockeeEntry.setRole("LOCKEE"); - lockHistoryRepository.save(lockeeEntry); - - // Eintrag für die Keyholderin + // Gemeinsamer History-Eintrag mit Teilnehmerliste + GameHistoryEntity entry = new GameHistoryEntity(); + entry.setGameType(de.oaa.xxx.games.history.GameType.CARDLOCK); + entry.setGameName(lock.getName()); + entry.setStartTime(lock.getStartTime()); + entry.setEndTime(lock.getUnlockTime()); + entry.setDurationMinutes(durationMinutes); + entry.addParticipant(lock.getLockee(), de.oaa.xxx.games.history.GameRole.LOCKEE); if (lock.getKeyholder() != null) { - LockHistoryEntity khEntry = new LockHistoryEntity(); - khEntry.setUserId(lock.getKeyholder()); - khEntry.setLockedBy(lock.getLockee()); - khEntry.setLockName(lock.getName()); - khEntry.setStartTime(lock.getStartTime()); - khEntry.setEndTime(lock.getUnlockTime()); - khEntry.setType(LockType.CARD); - khEntry.setDurationMinutes(durationMinutes); - khEntry.setRole("KEYHOLDER"); - lockHistoryRepository.save(khEntry); + entry.addParticipant(lock.getKeyholder(), de.oaa.xxx.games.history.GameRole.KEYHOLDER); } + gameHistoryRepository.save(entry); int minutes = (int) durationMinutes; userRepository.findById(lock.getLockee()).ifPresent(u -> { diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java index aada089..0bf9ecb 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java @@ -3,10 +3,8 @@ package de.oaa.xxx.games.chastity.cardlock; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.Task; -import de.oaa.xxx.social.SseService; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; -import de.oaa.xxx.social.entity.MessageEntity; -import de.oaa.xxx.social.repository.MessageRepository; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -25,8 +23,7 @@ public class TaskCardController { private final CommunityTaskVoteRepository communityTaskVoteRepository; private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository; private final AssignedTaskRepository assignedTaskRepository; - private final MessageRepository messageRepository; - private final SseService sseService; + private final SystemMessageService systemMessageService; public TaskCardController(CardlockRepository cardlockRepository, UserRepository userRepository, @@ -34,16 +31,14 @@ public class TaskCardController { CommunityTaskVoteRepository communityTaskVoteRepository, CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository, AssignedTaskRepository assignedTaskRepository, - MessageRepository messageRepository, - SseService sseService) { + SystemMessageService systemMessageService) { this.cardlockRepository = cardlockRepository; this.userRepository = userRepository; this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; this.communityTaskVoteRepository = communityTaskVoteRepository; this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository; this.assignedTaskRepository = assignedTaskRepository; - this.messageRepository = messageRepository; - this.sseService = sseService; + this.systemMessageService = systemMessageService; } // ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ───────────────── @@ -240,16 +235,6 @@ public class TaskCardController { } private void sendMessage(UUID fromId, UUID toId, String text, String targetUrl) { - if (toId == null) return; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(java.util.UUID.randomUUID()); - msg.setSenderId(fromId); - msg.setReceiverId(toId); - msg.setText(text); - msg.setSystemMessage(true); - msg.setTargetUrl(targetUrl); - msg.setSentAt(java.time.LocalDateTime.now()); - messageRepository.save(msg); - sseService.push(toId, "notification", Map.of("text", text)); + systemMessageService.send(fromId, toId, text, targetUrl, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java index d9faf5b..63cead2 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java @@ -4,7 +4,6 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.UUID; @@ -17,9 +16,7 @@ import org.springframework.transaction.annotation.Transactional; import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.tasks.AssignedTaskRepository; import de.oaa.xxx.games.chastity.tasks.Task; -import de.oaa.xxx.social.SseService; -import de.oaa.xxx.social.entity.MessageEntity; -import de.oaa.xxx.social.repository.MessageRepository; +import de.oaa.xxx.social.SystemMessageService; @Component public class TaskVoteScheduler { @@ -30,21 +27,18 @@ public class TaskVoteScheduler { private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository; private final CardlockRepository cardlockRepository; private final AssignedTaskRepository assignedTaskRepository; - private final MessageRepository messageRepository; - private final SseService sseService; + private final SystemMessageService systemMessageService; public TaskVoteScheduler(CommunityTaskVoteRepository communityTaskVoteRepository, CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository, CardlockRepository cardlockRepository, AssignedTaskRepository assignedTaskRepository, - MessageRepository messageRepository, - SseService sseService) { + SystemMessageService systemMessageService) { this.communityTaskVoteRepository = communityTaskVoteRepository; this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository; this.cardlockRepository = cardlockRepository; this.assignedTaskRepository = assignedTaskRepository; - this.messageRepository = messageRepository; - this.sseService = sseService; + this.systemMessageService = systemMessageService; } @Scheduled(fixedDelay = 60_000) @@ -117,16 +111,6 @@ public class TaskVoteScheduler { } private void sendMessage(UUID toId, String text, String targetUrl) { - if (toId == null) return; - MessageEntity msg = new MessageEntity(); - msg.setMessageId(UUID.randomUUID()); - msg.setSenderId(toId); // System-Nachricht, kein echter Sender - msg.setReceiverId(toId); - msg.setText(text); - msg.setSystemMessage(true); - msg.setTargetUrl(targetUrl); - msg.setSentAt(LocalDateTime.now()); - messageRepository.save(msg); - sseService.push(toId, "notification", Map.of("text", text)); + systemMessageService.send(toId, toId, text, targetUrl, de.oaa.xxx.social.entity.MessageCause.GAME_STATE); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryController.java deleted file mode 100644 index 0d3e6e4..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryController.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.oaa.xxx.games.chastity.history; - -import de.oaa.xxx.user.UserRepository; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.security.Principal; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -@RestController -@RequestMapping("/lockhistory") -public class LockHistoryController { - - private final UserRepository userRepository; - private final LockHistoryRepository lockHistoryRepository; - - public LockHistoryController(UserRepository userRepository, LockHistoryRepository lockHistoryRepository) { - this.userRepository = userRepository; - this.lockHistoryRepository = lockHistoryRepository; - } - - @GetMapping - public ResponseEntity>> get(@RequestParam UUID userId, Principal principal) { - var meOpt = userRepository.findByEmail(principal.getName()); - if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); - - var result = lockHistoryRepository.findByUserIdOrderByEndTimeDesc(userId).stream() - .map(e -> { - Map item = new LinkedHashMap<>(); - item.put("role", e.getRole()); - item.put("lockName", e.getLockName() != null ? e.getLockName() : ""); - item.put("startTime", e.getStartTime().toString()); - item.put("unlockTime", e.getEndTime().toString()); - item.put("durationMinutes", e.getDurationMinutes()); - if (e.getLockedBy() != null) { - userRepository.findById(e.getLockedBy()).ifPresent(u -> { - if ("LOCKEE".equals(e.getRole())) { - item.put("keyholderName", u.getName()); - } else { - item.put("lockeeName", u.getName()); - } - if (u.getProfilePicture() != null) { - item.put("partnerPic", u.getProfilePicture()); - } - }); - } - return item; - }).toList(); - return ResponseEntity.ok(result); - } -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryDTO.java deleted file mode 100644 index b2fb49b..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryDTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.oaa.xxx.games.chastity.history; - -import java.time.LocalDateTime; -import java.util.UUID; - -import de.oaa.xxx.games.chastity.LockType; - -public record LockHistoryDTO (UUID historyId, UUID userId, LocalDateTime startTime, LocalDateTime endTime, LockType type, UUID lockedBy, String lockName, long durationMinutes, String role) { - -} - diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryEntity.java deleted file mode 100644 index a90b6b1..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryEntity.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.oaa.xxx.games.chastity.history; - -import java.time.LocalDateTime; -import java.util.UUID; - -import de.oaa.xxx.games.chastity.LockType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -@Table(name = "lock_history") -public class LockHistoryEntity { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - private UUID historyId; - @Column(nullable = false) - private UUID userId; - @Column(nullable = false) - private LocalDateTime startTime; - @Column(nullable = false) - private LocalDateTime endTime; - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private LockType type; - @Column - private UUID lockedBy; - - @Column - private String lockName; - - @Column(nullable = false, columnDefinition = "BIGINT DEFAULT 0") - private long durationMinutes; - - // LOCKEE oder KEYHOLDER - @Column(nullable = false, length = 20) - private String role; - - public LockHistoryDTO toLockHistory() { - return new LockHistoryDTO(historyId, userId, startTime, endTime, type, lockedBy, lockName, durationMinutes, role); - } -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryRepository.java deleted file mode 100644 index 4b35ab8..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/history/LockHistoryRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.oaa.xxx.games.chastity.history; - -import java.util.List; -import java.util.UUID; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface LockHistoryRepository extends JpaRepository { - - List findByUserIdOrderByEndTimeDesc(UUID userId); - -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryController.java new file mode 100644 index 0000000..8f39527 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryController.java @@ -0,0 +1,82 @@ +package de.oaa.xxx.games.history; + +import de.oaa.xxx.user.UserRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RestController +@RequestMapping("/gamehistory") +public class GameHistoryController { + + private final UserRepository userRepository; + private final GameHistoryRepository gameHistoryRepository; + + public GameHistoryController(UserRepository userRepository, GameHistoryRepository gameHistoryRepository) { + this.userRepository = userRepository; + this.gameHistoryRepository = gameHistoryRepository; + } + + @GetMapping + @Transactional(readOnly = true) + public ResponseEntity>> get(@RequestParam UUID userId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + + var result = gameHistoryRepository.findByParticipantUserId(userId).stream() + .map(e -> { + Map item = new LinkedHashMap<>(); + item.put("historyId", e.getHistoryId()); + item.put("gameType", e.getGameType()); + item.put("gameName", e.getGameName() != null ? e.getGameName() : ""); + item.put("lockName", e.getGameName() != null ? e.getGameName() : ""); + item.put("startTime", e.getStartTime().toString()); + item.put("unlockTime", e.getEndTime().toString()); + item.put("durationMinutes", e.getDurationMinutes()); + + List> participants = e.getParticipants().stream() + .map(p -> { + Map pm = new LinkedHashMap<>(); + pm.put("userId", p.getUserId()); + pm.put("role", p.getRole()); + userRepository.findById(p.getUserId()).ifPresent(u -> { + pm.put("name", u.getName()); + pm.put("picture", u.getProfilePicture()); + }); + return pm; + }) + .toList(); + item.put("participants", participants); + + // Abwärtskompatible Felder für benutzer.html (wird später angepasst) + e.getParticipants().stream() + .filter(p -> p.getUserId().equals(userId)) + .findFirst() + .ifPresent(own -> item.put("role", own.getRole().name())); + + e.getParticipants().stream() + .filter(p -> !p.getUserId().equals(userId)) + .findFirst() + .ifPresent(partner -> userRepository.findById(partner.getUserId()).ifPresent(u -> { + if (GameRole.LOCKEE == partner.getRole()) { + item.put("lockeeName", u.getName()); + } else if (GameRole.KEYHOLDER == partner.getRole()) { + item.put("keyholderName", u.getName()); + } + item.put("partnerPic", u.getProfilePicture()); + })); + + return item; + }).toList(); + return ResponseEntity.ok(result); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryDTO.java new file mode 100644 index 0000000..2849c6b --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryDTO.java @@ -0,0 +1,17 @@ +package de.oaa.xxx.games.history; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public record GameHistoryDTO( + UUID historyId, + GameType gameType, + LocalDateTime startTime, + LocalDateTime endTime, + String gameName, + long durationMinutes, + List participants +) { + public record ParticipantDTO(UUID userId, GameRole role) {} +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryEntity.java new file mode 100644 index 0000000..f8f57f5 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryEntity.java @@ -0,0 +1,49 @@ +package de.oaa.xxx.games.history; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "game_history") +public class GameHistoryEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column + private UUID historyId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + private GameType gameType; + + @Column(nullable = false) + private LocalDateTime startTime; + + @Column(nullable = false) + private LocalDateTime endTime; + + @Column + private String gameName; + + @Column(nullable = false, columnDefinition = "BIGINT DEFAULT 0") + private long durationMinutes; + + @OneToMany(mappedBy = "history", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + private List participants = new ArrayList<>(); + + public void addParticipant(UUID userId, GameRole role) { + GameHistoryParticipantEntity p = new GameHistoryParticipantEntity(); + p.setUserId(userId); + p.setRole(role); + p.setHistory(this); + participants.add(p); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantEntity.java new file mode 100644 index 0000000..a5ad051 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantEntity.java @@ -0,0 +1,30 @@ +package de.oaa.xxx.games.history; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "game_history_participant") +public class GameHistoryParticipantEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column + private UUID participantId; + + @Column(nullable = false) + private UUID userId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + private GameRole role; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "history_id", nullable = false) + private GameHistoryEntity history; +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantRepository.java new file mode 100644 index 0000000..b64881f --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryParticipantRepository.java @@ -0,0 +1,8 @@ +package de.oaa.xxx.games.history; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface GameHistoryParticipantRepository extends JpaRepository { +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryRepository.java new file mode 100644 index 0000000..d4a81fc --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameHistoryRepository.java @@ -0,0 +1,17 @@ +package de.oaa.xxx.games.history; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.UUID; + +public interface GameHistoryRepository extends JpaRepository { + + @Query("SELECT DISTINCT h FROM GameHistoryEntity h JOIN h.participants p WHERE p.userId = :userId ORDER BY h.endTime DESC") + List findByParticipantUserId(@Param("userId") UUID userId); + + @Query("SELECT DISTINCT h FROM GameHistoryEntity h JOIN h.participants p WHERE p.userId = :userId AND h.gameType = :gameType ORDER BY h.endTime DESC") + List findByParticipantUserIdAndGameType(@Param("userId") UUID userId, @Param("gameType") GameType gameType); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameRole.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameRole.java new file mode 100644 index 0000000..f7e5562 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameRole.java @@ -0,0 +1,7 @@ +package de.oaa.xxx.games.history; + +public enum GameRole { + LOCKEE, + KEYHOLDER, + PLAYER +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameType.java b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameType.java new file mode 100644 index 0000000..04d1a86 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/history/GameType.java @@ -0,0 +1,8 @@ +package de.oaa.xxx.games.history; + +public enum GameType { + CARDLOCK, + TIMELOCK, + BDSM, + VANILLA +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/mail/MailTemplateService.java b/xxxthegame/src/main/java/de/oaa/xxx/mail/MailTemplateService.java index e4b8692..ec681d2 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/mail/MailTemplateService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/mail/MailTemplateService.java @@ -165,6 +165,56 @@ public class MailTemplateService { ); } + public String buildNotificationMail(String name, String text, String targetUrl, String baseUrl) { + String actionButton = targetUrl != null + ? """ + + """.formatted(baseUrl, targetUrl, colorPrimary) + : "
"; + + String settingsUrl = baseUrl + "/einstellungen.html#sec-benachrichtigungen"; + + return """ + + + +
+ +

XXX The Game

+ +

Hallo %s,

+

%s

+ + %s + +
+ +

+ Du erhältst diese E-Mail, weil du E-Mail-Benachrichtigungen für diese Kategorie aktiviert hast. + Du kannst deine Einstellungen jederzeit unter + Einstellungen → Benachrichtigungen anpassen. +

+
+ + + """.formatted( + colorBg, colorText, + colorCard, colorSecondary, + colorPrimary, + colorText, name, + colorText, text, + actionButton, + colorSecondary, + colorMuted, settingsUrl, colorPrimary + ); + } + public String buildActivationMail(String name, String activationLink, String activatePageUrl, String uuid) { return """ diff --git a/xxxthegame/src/main/java/de/oaa/xxx/registration/ActivationController.java b/xxxthegame/src/main/java/de/oaa/xxx/registration/ActivationController.java index 16382f2..fc3d067 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/registration/ActivationController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/registration/ActivationController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import de.oaa.xxx.user.Registration; import de.oaa.xxx.user.UserController; @@ -29,12 +28,7 @@ public class ActivationController { public ResponseEntity activate(@PathVariable String uuid) { RegistrationEntity registration = registrationRepository.findById(UUID.fromString(uuid)).orElse(null); if (registration != null && !Boolean.TRUE.equals(registration.getActivated())) { - Registration reg = new Registration(); - reg.setEmail(registration.getEmail()); - reg.setName(registration.getName()); - reg.setPasswordHash(registration.getPassword()); - - ResponseEntity response = userController.userAnlegen(reg); + ResponseEntity response = userController.userAnlegen(registration.toRegistration()); if (response.getStatusCode().is2xxSuccessful()) { registration.setActivated(Boolean.TRUE); registrationRepository.save(registration); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/registration/Registration.java b/xxxthegame/src/main/java/de/oaa/xxx/registration/Registration.java index 3bc5227..182b551 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/registration/Registration.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/registration/Registration.java @@ -3,6 +3,7 @@ package de.oaa.xxx.registration; import lombok.Getter; import lombok.Setter; +import java.time.LocalDate; import java.util.UUID; @Getter @@ -13,6 +14,7 @@ public class Registration { private String name; private String email; private String passwordHash; + private LocalDate geburtsdatum; @Override public String toString() { diff --git a/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationController.java b/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationController.java index c2c0f0b..000efe3 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationController.java @@ -14,6 +14,9 @@ import de.oaa.xxx.mail.MailService; import de.oaa.xxx.mail.MailTemplateService; import de.oaa.xxx.user.UserRepository; +import java.time.LocalDate; +import java.time.Period; + @RestController @RequestMapping("/registration") public class RegistrationController { @@ -39,6 +42,11 @@ public class RegistrationController { @PostMapping public ResponseEntity create(@RequestBody Registration registration) { LOGGER.info("POST {}: {}", getClass().getName(), registration); + if (registration.getGeburtsdatum() == null + || Period.between(registration.getGeburtsdatum(), LocalDate.now()).getYears() < 18) { + LOGGER.warn("Registrierung abgelehnt – Mindestalter nicht erreicht"); + return ResponseEntity.status(422).build(); + } if (registrationRepository.findByEmail(registration.getEmail()).isPresent() || userRepository.findByEmail(registration.getEmail()).isPresent()) { LOGGER.warn("User mit E-Mail {} bereits vorhanden", registration.getEmail()); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationEntity.java index b15496c..a943ba2 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/registration/RegistrationEntity.java @@ -7,6 +7,7 @@ import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; +import java.time.LocalDate; import java.util.UUID; @Getter @@ -26,6 +27,8 @@ public class RegistrationEntity { private String password; @Column private Boolean activated; + @Column + private LocalDate geburtsdatum; @Override public String toString() { @@ -38,6 +41,7 @@ public class RegistrationEntity { registration.setEmail(email); registration.setName(name); registration.setPasswordHash(password); + registration.setGeburtsdatum(geburtsdatum); return registration; } @@ -48,6 +52,7 @@ public class RegistrationEntity { entity.setActivated(Boolean.FALSE); entity.setName(registration.getName()); entity.setPassword(registration.getPasswordHash()); + entity.setGeburtsdatum(registration.getGeburtsdatum()); return entity; } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/SessionRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/session/repository/SessionRepository.java deleted file mode 100644 index fb8fdd6..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/session/repository/SessionRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.oaa.xxx.session.repository; - -import de.oaa.xxx.session.entity.SessionEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; -import java.util.UUID; - -public interface SessionRepository extends JpaRepository { - - Optional findByUserId(UUID userId); -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/SocialController.java b/xxxthegame/src/main/java/de/oaa/xxx/social/SocialController.java index 9c71992..8c65bb0 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/social/SocialController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/SocialController.java @@ -6,6 +6,7 @@ import de.oaa.xxx.social.dto.MessageDto; import de.oaa.xxx.social.dto.UserProfile; import de.oaa.xxx.social.entity.FriendshipEntity; import de.oaa.xxx.social.entity.FriendshipEntity.Status; +import de.oaa.xxx.social.entity.MessageCause; import de.oaa.xxx.social.entity.MessageEntity; import de.oaa.xxx.social.repository.FriendshipRepository; import de.oaa.xxx.social.repository.MessageRepository; @@ -31,15 +32,18 @@ public class SocialController { private final FriendshipRepository friendshipRepository; private final MessageRepository messageRepository; private final SseService sseService; + private final SystemMessageService systemMessageService; public SocialController(UserRepository userRepository, FriendshipRepository friendshipRepository, MessageRepository messageRepository, - SseService sseService) { + SseService sseService, + SystemMessageService systemMessageService) { this.userRepository = userRepository; this.friendshipRepository = friendshipRepository; this.messageRepository = messageRepository; this.sseService = sseService; + this.systemMessageService = systemMessageService; } record FriendRequestBody(UUID receiverId) {} @@ -94,6 +98,13 @@ public class SocialController { f.setCreatedAt(LocalDateTime.now()); friendshipRepository.save(f); LOGGER.info("User {} hat Freundschaftsanfrage an User {} gesendet", myId, body.receiverId()); + + String senderName = meOpt.get().getName(); + systemMessageService.send(myId, body.receiverId(), + senderName + " hat dir eine Freundschaftsanfrage gesendet.", + "/benutzer.html?userId=" + myId, + MessageCause.FRIENDREQUEST); + return ResponseEntity.status(201).build(); } @@ -299,22 +310,52 @@ public class SocialController { // ── Helpers ── private UserProfile toUserProfileWithStatus(UserEntity user, UUID myId) { + boolean isOwn = user.getUserId().equals(myId); String status = "NONE"; - var existing = friendshipRepository.findExisting(myId, user.getUserId()); - if (existing.isPresent()) { - FriendshipEntity f = existing.get(); - if (f.getStatus() == Status.ACCEPTED) { - status = "FRIEND"; - } else if (f.getSenderId().equals(myId)) { - status = "PENDING_SENT"; - } else { - status = "PENDING_RECEIVED"; + if (!isOwn) { + var existing = friendshipRepository.findExisting(myId, user.getUserId()); + if (existing.isPresent()) { + FriendshipEntity f = existing.get(); + if (f.getStatus() == Status.ACCEPTED) { + status = "FRIEND"; + } else if (f.getSenderId().equals(myId)) { + status = "PENDING_SENT"; + } else { + status = "PENDING_RECEIVED"; + } } } - return new UserProfile(user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(), - status, user.getAlter(), user.getGroesse(), user.getGewicht(), - user.getGeschlecht(), user.getNeigung(), user.getBeziehungsstatus(), user.getBeschreibung(), - user.getLockeeXp(), user.getKeyholderXp()); + boolean isFriend = isOwn || "FRIEND".equals(status); + + // Grunddaten nur zurückgeben wenn berechtigt + de.oaa.xxx.user.Sichtbarkeit svGd = user.getSichtbarkeitGrunddaten(); + boolean showGrunddaten = isOwn || svGd == de.oaa.xxx.user.Sichtbarkeit.ALLE + || (svGd == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend); + + // XP nur zurückgeben wenn berechtigt + de.oaa.xxx.user.Sichtbarkeit svXp = user.getSichtbarkeitXp(); + boolean showXp = isOwn || svXp == de.oaa.xxx.user.Sichtbarkeit.ALLE + || (svXp == de.oaa.xxx.user.Sichtbarkeit.NUR_FREUNDE && isFriend); + + return new UserProfile( + user.getUserId(), user.getName(), user.getProfilePicture(), user.getProfilePictureHq(), + status, + showGrunddaten ? user.getAlter() : null, + showGrunddaten ? user.getGroesse() : null, + showGrunddaten ? user.getGewicht() : null, + showGrunddaten ? user.getGeschlecht() : null, + showGrunddaten ? user.getNeigung() : null, + showGrunddaten ? user.getBeziehungsstatus() : null, + showGrunddaten ? user.getBeschreibung() : null, + showXp ? user.getLockeeXp() : 0, + showXp ? user.getKeyholderXp() : 0, + user.getSichtbarkeitGrunddaten(), + user.getSichtbarkeitGalerie(), + user.getSichtbarkeitFreunde(), + user.getSichtbarkeitFeed(), + user.getSichtbarkeitPinnwand(), + user.getSichtbarkeitXp(), + user.getSichtbarkeitLockhistorie()); } private MessageDto toMessageDto(MessageEntity m) { diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/SystemMessageService.java b/xxxthegame/src/main/java/de/oaa/xxx/social/SystemMessageService.java new file mode 100644 index 0000000..6113b0b --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/SystemMessageService.java @@ -0,0 +1,103 @@ +package de.oaa.xxx.social; + +import de.oaa.xxx.mail.Email; +import de.oaa.xxx.mail.MailService; +import de.oaa.xxx.mail.MailTemplateService; +import org.springframework.beans.factory.annotation.Value; +import de.oaa.xxx.social.entity.MessageCause; +import de.oaa.xxx.social.entity.MessageEntity; +import de.oaa.xxx.social.entity.NotificationPreferenceEntity; +import de.oaa.xxx.social.repository.MessageRepository; +import de.oaa.xxx.social.repository.NotificationPreferenceRepository; +import de.oaa.xxx.user.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.UUID; + +@Service +public class SystemMessageService { + + private static final Logger LOGGER = LoggerFactory.getLogger(SystemMessageService.class); + + private final MessageRepository messageRepository; + private final NotificationPreferenceRepository preferenceRepository; + private final UserRepository userRepository; + private final SseService sseService; + private final MailService mailService; + private final MailTemplateService mailTemplateService; + + @Value("${app.base-url:http://localhost:8080}") + private String baseUrl; + + public SystemMessageService(MessageRepository messageRepository, + NotificationPreferenceRepository preferenceRepository, + UserRepository userRepository, + SseService sseService, + MailService mailService, + MailTemplateService mailTemplateService) { + this.messageRepository = messageRepository; + this.preferenceRepository = preferenceRepository; + this.userRepository = userRepository; + this.sseService = sseService; + this.mailService = mailService; + this.mailTemplateService = mailTemplateService; + } + + /** + * Sendet eine Systemnachricht unter Berücksichtigung der Benachrichtigungseinstellungen des Empfängers. + */ + public void send(UUID senderId, UUID receiverId, String text, String targetUrl, MessageCause cause) { + if (senderId == null || receiverId == null) return; + + NotificationPreferenceEntity pref = preferenceRepository + .findByUserIdAndCause(receiverId, cause) + .orElseGet(() -> NotificationPreferenceEntity.defaultFor(receiverId, cause)); + + // FRIENDREQUEST ist immer in-app, unabhängig von der Einstellung + boolean sendInApp = cause == MessageCause.FRIENDREQUEST || pref.isInApp(); + + if (sendInApp) { + MessageEntity msg = new MessageEntity(); + msg.setMessageId(UUID.randomUUID()); + msg.setSenderId(senderId); + msg.setReceiverId(receiverId); + msg.setText(text); + msg.setSentAt(LocalDateTime.now()); + msg.setSystemMessage(true); + msg.setMessageCause(cause); + if (targetUrl != null) msg.setTargetUrl(targetUrl); + messageRepository.save(msg); + + long unread = messageRepository.countByReceiverIdAndSystemMessageAndReadAtIsNull(receiverId, true); + sseService.push(receiverId, "NOTIFICATION", Map.of("unreadCount", unread, "text", text)); + } + + if (pref.isEmail()) { + userRepository.findById(receiverId).ifPresent(user -> { + try { + Email email = new Email(); + email.setEmailAdresse(user.getEmail()); + email.setTitel(causeTitel(cause)); + email.setText(mailTemplateService.buildNotificationMail(user.getName(), text, targetUrl, baseUrl)); + mailService.send(email); + } catch (Exception e) { + LOGGER.error("E-Mail-Benachrichtigung fehlgeschlagen für userId={}: {}", receiverId, e.getMessage()); + } + }); + } + } + + private String causeTitel(MessageCause cause) { + return switch (cause) { + case INVITATION -> "XXX The Game – Neue Einladung"; + case GAME_STATE -> "XXX The Game – Spielstatus-Änderung"; + case EMERGENCY -> "XXX The Game – ⚠️ Notfall"; + case FRIENDREQUEST -> "XXX The Game – Neue Freundschaftsanfrage"; + }; + } + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/dto/UserProfile.java b/xxxthegame/src/main/java/de/oaa/xxx/social/dto/UserProfile.java index 4edc020..9f22920 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/social/dto/UserProfile.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/dto/UserProfile.java @@ -3,6 +3,7 @@ package de.oaa.xxx.social.dto; import de.oaa.xxx.user.Beziehungsstatus; import de.oaa.xxx.user.Geschlecht; import de.oaa.xxx.user.Neigung; +import de.oaa.xxx.user.Sichtbarkeit; import java.util.UUID; @@ -20,10 +21,20 @@ public record UserProfile( Beziehungsstatus beziehungsstatus, String beschreibung, int lockeeXp, - int keyholderXp + int keyholderXp, + // Datenschutz-Einstellungen + Sichtbarkeit sichtbarkeitGrunddaten, + Sichtbarkeit sichtbarkeitGalerie, + Sichtbarkeit sichtbarkeitFreunde, + Sichtbarkeit sichtbarkeitFeed, + Sichtbarkeit sichtbarkeitPinnwand, + Sichtbarkeit sichtbarkeitXp, + Sichtbarkeit sichtbarkeitLockhistorie ) { /** Compact constructor for contexts where profile details are not needed (friend list etc.) */ public UserProfile(UUID userId, String name, String profilePicture, String profilePictureHq, String friendStatus) { - this(userId, name, profilePicture, profilePictureHq, friendStatus, null, null, null, null, null, null, null, 0, 0); + this(userId, name, profilePicture, profilePictureHq, friendStatus, + null, null, null, null, null, null, null, 0, 0, + null, null, null, null, null, null, null); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageCause.java b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageCause.java new file mode 100644 index 0000000..9bbbac6 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageCause.java @@ -0,0 +1,8 @@ +package de.oaa.xxx.social.entity; + +public enum MessageCause { + INVITATION, + GAME_STATE, + EMERGENCY, + FRIENDREQUEST +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageEntity.java index cd5805d..0027ed1 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/MessageEntity.java @@ -35,6 +35,10 @@ public class MessageEntity { @Column(nullable = false) private boolean systemMessage = false; + @Enumerated(EnumType.STRING) + @Column(length = 20) + private MessageCause messageCause; + @Column(length = 500) private String targetUrl; } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/entity/NotificationPreferenceEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/NotificationPreferenceEntity.java new file mode 100644 index 0000000..2fdc08e --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/entity/NotificationPreferenceEntity.java @@ -0,0 +1,40 @@ +package de.oaa.xxx.social.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "notification_preference", uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "cause"})) +public class NotificationPreferenceEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @Column(name = "user_id", nullable = false) + private UUID userId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false, length = 20) + private MessageCause cause; + + @Column(nullable = false) + private boolean inApp = true; + + @Column(nullable = false) + private boolean email = false; + + /** Erzeugt eine nicht persistierte Standardpräferenz für unbekannte/neue Causes. */ + public static NotificationPreferenceEntity defaultFor(UUID userId, MessageCause cause) { + NotificationPreferenceEntity p = new NotificationPreferenceEntity(); + p.setUserId(userId); + p.setCause(cause); + // inApp=true und email=false sind bereits die Java-Felddefaults + return p; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/social/repository/NotificationPreferenceRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/social/repository/NotificationPreferenceRepository.java new file mode 100644 index 0000000..2d2d1f9 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/social/repository/NotificationPreferenceRepository.java @@ -0,0 +1,16 @@ +package de.oaa.xxx.social.repository; + +import de.oaa.xxx.social.entity.MessageCause; +import de.oaa.xxx.social.entity.NotificationPreferenceEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface NotificationPreferenceRepository extends JpaRepository { + + List findByUserId(UUID userId); + + Optional findByUserIdAndCause(UUID userId, MessageCause cause); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/user/Registration.java b/xxxthegame/src/main/java/de/oaa/xxx/user/Registration.java deleted file mode 100644 index dc4d5c1..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/user/Registration.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.oaa.xxx.user; - -import lombok.Getter; -import lombok.Setter; - -import java.util.UUID; - -@Getter -@Setter -public class Registration { - - private UUID id; - private String name; - private String email; - private String passwordHash; - - @Override - public String toString() { - return "Registration [id=" + id + ", name=" + name + ", email=" + email + "]"; - } -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/user/Sichtbarkeit.java b/xxxthegame/src/main/java/de/oaa/xxx/user/Sichtbarkeit.java new file mode 100644 index 0000000..7bfb559 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/user/Sichtbarkeit.java @@ -0,0 +1,7 @@ +package de.oaa.xxx.user; + +public enum Sichtbarkeit { + ALLE, + NUR_FREUNDE, + NUR_ICH +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/user/User.java b/xxxthegame/src/main/java/de/oaa/xxx/user/User.java index e3c883a..5fced89 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/user/User.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/user/User.java @@ -3,6 +3,8 @@ package de.oaa.xxx.user; import lombok.Getter; import lombok.Setter; +import java.time.LocalDate; +import java.time.Period; import java.util.UUID; @Getter @@ -14,7 +16,7 @@ public class User { private String email; private String password; private String profilePicture; - private Integer alter; + private LocalDate geburtsdatum; private Integer groesse; private Integer gewicht; private Geschlecht geschlecht; @@ -22,6 +24,10 @@ public class User { private Beziehungsstatus beziehungsstatus; private String beschreibung; + public Integer getAlter() { + return geburtsdatum != null ? Period.between(geburtsdatum, LocalDate.now()).getYears() : null; + } + @Override public String toString() { return "User[userId=" + userId + ", name=" + name + ", email=" + email + "]"; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/user/UserController.java b/xxxthegame/src/main/java/de/oaa/xxx/user/UserController.java index 355cd9f..8f51478 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/user/UserController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/user/UserController.java @@ -1,8 +1,13 @@ package de.oaa.xxx.user; import java.security.Principal; +import java.time.LocalDate; +import java.time.Period; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,6 +15,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -17,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import de.oaa.xxx.aufgaben.repository.AufgabeRepository; +import de.oaa.xxx.games.bdsm.entity.BdsmDefaultsEntity; +import de.oaa.xxx.games.bdsm.repository.BdsmDefaultsRepository; import de.oaa.xxx.aufgaben.repository.AufgabenGruppeRepository; import de.oaa.xxx.aufgaben.repository.FavoritRepository; import de.oaa.xxx.aufgaben.repository.GruppenAboRepository; @@ -24,19 +32,23 @@ import de.oaa.xxx.aufgaben.repository.SperreRepository; import de.oaa.xxx.aufgaben.repository.StrafeRepository; import de.oaa.xxx.aufgaben.repository.ToyRepository; import de.oaa.xxx.emailchange.EmailChangeRepository; +import de.oaa.xxx.games.bdsm.entity.AktiveSperreEntity; +import de.oaa.xxx.games.bdsm.entity.MitspielerEntity; +import de.oaa.xxx.games.bdsm.repository.AktiveSperreRepository; +import de.oaa.xxx.games.bdsm.repository.BdsmGameRepository; +import de.oaa.xxx.games.bdsm.repository.MitspielerRepository; import de.oaa.xxx.passwordreset.PasswordResetRepository; +import de.oaa.xxx.registration.Registration; import de.oaa.xxx.registration.RegistrationRepository; -import de.oaa.xxx.session.entity.AktiveSperreEntity; -import de.oaa.xxx.session.entity.MitspielerEntity; -import de.oaa.xxx.session.repository.AktiveSperreRepository; -import de.oaa.xxx.session.repository.MitspielerRepository; -import de.oaa.xxx.session.repository.SessionRepository; -import de.oaa.xxx.social.repository.ProfileImageLikeRepository; -import de.oaa.xxx.social.repository.ProfileImageRepository; +import de.oaa.xxx.social.entity.MessageCause; +import de.oaa.xxx.social.entity.NotificationPreferenceEntity; +import de.oaa.xxx.social.repository.KommentarLikeRepository; +import de.oaa.xxx.social.repository.KommentarRepository; +import de.oaa.xxx.social.repository.NotificationPreferenceRepository; import de.oaa.xxx.social.repository.PinnwandEintragRepository; import de.oaa.xxx.social.repository.PinnwandLikeRepository; -import de.oaa.xxx.social.repository.KommentarRepository; -import de.oaa.xxx.social.repository.KommentarLikeRepository; +import de.oaa.xxx.social.repository.ProfileImageLikeRepository; +import de.oaa.xxx.social.repository.ProfileImageRepository; import jakarta.transaction.Transactional; @RestController @@ -54,7 +66,7 @@ public class UserController { private final ToyRepository toyRepository; private final FavoritRepository favoritRepository; private final GruppenAboRepository gruppenAboRepository; - private final SessionRepository sessionRepository; + private final BdsmGameRepository sessionRepository; private final AktiveSperreRepository aktiveSperreRepository; private final MitspielerRepository mitspielerRepository; private final EmailChangeRepository emailChangeRepository; @@ -65,6 +77,8 @@ public class UserController { private final PinnwandLikeRepository pinnwandLikeRepository; private final KommentarRepository kommentarRepository; private final KommentarLikeRepository kommentarLikeRepository; + private final NotificationPreferenceRepository notificationPreferenceRepository; + private final BdsmDefaultsRepository bdsmDefaultsRepository; public UserController(UserRepository userRepository, RegistrationRepository registrationRepository, @@ -75,7 +89,7 @@ public class UserController { ToyRepository toyRepository, FavoritRepository favoritRepository, GruppenAboRepository gruppenAboRepository, - SessionRepository sessionRepository, + BdsmGameRepository sessionRepository, AktiveSperreRepository aktiveSperreRepository, MitspielerRepository mitspielerRepository, EmailChangeRepository emailChangeRepository, @@ -85,7 +99,9 @@ public class UserController { PinnwandEintragRepository pinnwandEintragRepository, PinnwandLikeRepository pinnwandLikeRepository, KommentarRepository kommentarRepository, - KommentarLikeRepository kommentarLikeRepository) { + KommentarLikeRepository kommentarLikeRepository, + NotificationPreferenceRepository notificationPreferenceRepository, + BdsmDefaultsRepository bdsmDefaultsRepository) { this.userRepository = userRepository; this.registrationRepository = registrationRepository; this.aufgabenGruppeRepository = aufgabenGruppeRepository; @@ -106,12 +122,23 @@ public class UserController { this.pinnwandLikeRepository = pinnwandLikeRepository; this.kommentarRepository = kommentarRepository; this.kommentarLikeRepository = kommentarLikeRepository; + this.notificationPreferenceRepository = notificationPreferenceRepository; + this.bdsmDefaultsRepository = bdsmDefaultsRepository; } record ProfilePictureRequest(String picture, String pictureHq) {} record NameChangeRequest(String name) {} - record ProfileRequest(Integer alter, Integer groesse, Integer gewicht, + record GeburtsdatumChangeRequest(LocalDate geburtsdatum) {} + record ProfileRequest(Integer groesse, Integer gewicht, Geschlecht geschlecht, Neigung neigung, Beziehungsstatus beziehungsstatus, String beschreibung) {} + record PrivacyRequest( + Sichtbarkeit sichtbarkeitGrunddaten, + Sichtbarkeit sichtbarkeitGalerie, + Sichtbarkeit sichtbarkeitFreunde, + Sichtbarkeit sichtbarkeitFeed, + Sichtbarkeit sichtbarkeitPinnwand, + Sichtbarkeit sichtbarkeitXp, + Sichtbarkeit sichtbarkeitLockhistorie) {} @PutMapping("/me/picture") public ResponseEntity updateProfilePicture(@RequestBody ProfilePictureRequest request, Principal principal) { @@ -132,7 +159,6 @@ public class UserController { if (request.beschreibung() != null && request.beschreibung().length() > 600) { return ResponseEntity.badRequest().build(); } - user.setAlter(request.alter()); user.setGroesse(request.groesse()); user.setGewicht(request.gewicht()); user.setGeschlecht(request.geschlecht()); @@ -144,6 +170,126 @@ public class UserController { return ResponseEntity.ok().build(); } + @PutMapping("/me/privacy") + public ResponseEntity updatePrivacy(@RequestBody PrivacyRequest request, Principal principal) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + var user = userOpt.get(); + if (request.sichtbarkeitGrunddaten() != null) user.setSichtbarkeitGrunddaten(request.sichtbarkeitGrunddaten()); + if (request.sichtbarkeitGalerie() != null) user.setSichtbarkeitGalerie(request.sichtbarkeitGalerie()); + if (request.sichtbarkeitFreunde() != null) user.setSichtbarkeitFreunde(request.sichtbarkeitFreunde()); + if (request.sichtbarkeitFeed() != null) user.setSichtbarkeitFeed(request.sichtbarkeitFeed()); + if (request.sichtbarkeitPinnwand() != null) user.setSichtbarkeitPinnwand(request.sichtbarkeitPinnwand()); + if (request.sichtbarkeitXp() != null) user.setSichtbarkeitXp(request.sichtbarkeitXp()); + if (request.sichtbarkeitLockhistorie()!= null) user.setSichtbarkeitLockhistorie(request.sichtbarkeitLockhistorie()); + userRepository.save(user); + LOGGER.info("User {} hat Datenschutz-Einstellungen aktualisiert", user.getUserId()); + return ResponseEntity.ok().build(); + } + + record NotificationPreferenceRequest(boolean inApp, boolean email) {} + + @GetMapping("/me/notifications") + public ResponseEntity> getNotifications(Principal principal) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID userId = userOpt.get().getUserId(); + + Map byKey = notificationPreferenceRepository.findByUserId(userId) + .stream().collect(Collectors.toMap(p -> p.getCause().name(), p -> p)); + + Map result = new LinkedHashMap<>(); + for (MessageCause cause : MessageCause.values()) { + NotificationPreferenceEntity pref = byKey.getOrDefault( + cause.name(), NotificationPreferenceEntity.defaultFor(userId, cause)); + Map entry = new LinkedHashMap<>(); + entry.put("inApp", pref.isInApp()); + entry.put("email", pref.isEmail()); + result.put(cause.name(), entry); + } + return ResponseEntity.ok(result); + } + + @PutMapping("/me/notifications") + public ResponseEntity updateNotifications(@RequestBody Map request, Principal principal) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID userId = userOpt.get().getUserId(); + + for (var entry : request.entrySet()) { + MessageCause cause; + try { + cause = MessageCause.valueOf(entry.getKey()); + } catch (IllegalArgumentException e) { + continue; + } + NotificationPreferenceEntity pref = notificationPreferenceRepository + .findByUserIdAndCause(userId, cause) + .orElseGet(() -> { + NotificationPreferenceEntity n = new NotificationPreferenceEntity(); + n.setUserId(userId); + n.setCause(cause); + return n; + }); + pref.setInApp(entry.getValue().inApp()); + pref.setEmail(entry.getValue().email()); + notificationPreferenceRepository.save(pref); + } + return ResponseEntity.ok().build(); + } + + record BdsmDefaultsRequest(List spieltMit, List rollen, List werkzeuge) {} + + @GetMapping("/me/bdsm-defaults") + public ResponseEntity> getBdsmDefaults(Principal principal) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID userId = userOpt.get().getUserId(); + + BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId) + .orElse(new BdsmDefaultsEntity()); + Map result = new java.util.LinkedHashMap<>(); + result.put("spieltMit", splitOrEmpty(d.getSpieltMit())); + result.put("rollen", splitOrEmpty(d.getRollen())); + result.put("werkzeuge", splitOrEmpty(d.getWerkzeuge())); + return ResponseEntity.ok(result); + } + + @PutMapping("/me/bdsm-defaults") + public ResponseEntity updateBdsmDefaults(@RequestBody BdsmDefaultsRequest request, Principal principal) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID userId = userOpt.get().getUserId(); + + BdsmDefaultsEntity d = bdsmDefaultsRepository.findByUserId(userId) + .orElseGet(() -> { BdsmDefaultsEntity n = new BdsmDefaultsEntity(); n.setUserId(userId); return n; }); + d.setSpieltMit(request.spieltMit() == null ? "" : String.join(",", request.spieltMit())); + d.setRollen(request.rollen() == null ? "" : String.join(",", request.rollen())); + d.setWerkzeuge(request.werkzeuge() == null ? "" : String.join(",", request.werkzeuge())); + bdsmDefaultsRepository.save(d); + return ResponseEntity.ok().build(); + } + + private static List splitOrEmpty(String s) { + if (s == null || s.isBlank()) return List.of(); + return List.of(s.split(",")); + } + + @PutMapping("/me/geburtsdatum") + public ResponseEntity updateGeburtsdatum(@RequestBody GeburtsdatumChangeRequest request, Principal principal) { + if (request.geburtsdatum() == null + || Period.between(request.geburtsdatum(), LocalDate.now()).getYears() < 18) { + return ResponseEntity.status(422).build(); + } + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isEmpty()) return ResponseEntity.status(401).build(); + var user = userOpt.get(); + user.setGeburtsdatum(request.geburtsdatum()); + userRepository.save(user); + LOGGER.info("User {} hat Geburtsdatum aktualisiert", user.getUserId()); + return ResponseEntity.ok().build(); + } + @PutMapping("/me/name") public ResponseEntity updateName(@RequestBody NameChangeRequest request, Principal principal) { String newName = request.name(); @@ -264,7 +410,14 @@ public class UserController { entity.setEmail(registration.getEmail()); entity.setName(registration.getName()); entity.setPassword(registration.getPasswordHash()); + entity.setGeburtsdatum(registration.getGeburtsdatum()); userRepository.save(entity); + + for (MessageCause cause : MessageCause.values()) { + notificationPreferenceRepository.save( + NotificationPreferenceEntity.defaultFor(entity.getUserId(), cause)); + } + return ResponseEntity.status(201).build(); } catch (Exception exception) { LOGGER.error(exception.getMessage(), exception); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/user/UserEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/user/UserEntity.java index c05a140..13e1c04 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/user/UserEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/user/UserEntity.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import java.time.LocalDate; +import java.time.Period; import java.util.UUID; @Getter @@ -28,8 +30,8 @@ public class UserEntity { @Column(columnDefinition = "MEDIUMTEXT") private String profilePictureHq; - @Column(name = "benutzer_alter") - private Integer alter; + @Column + private LocalDate geburtsdatum; @Column private Integer groesse; @@ -58,6 +60,39 @@ public class UserEntity { @Column(nullable = false, columnDefinition = "INT DEFAULT 0") private int keyholderXp; + // ── Datenschutz / Sichtbarkeit ── + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitGrunddaten = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitGalerie = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitFreunde = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitFeed = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitPinnwand = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitXp = Sichtbarkeit.ALLE; + + @Enumerated(EnumType.STRING) + @Column(length = 20, nullable = false, columnDefinition = "VARCHAR(20) DEFAULT 'ALLE'") + private Sichtbarkeit sichtbarkeitLockhistorie = Sichtbarkeit.ALLE; + + public Integer getAlter() { + return geburtsdatum != null ? Period.between(geburtsdatum, LocalDate.now()).getYears() : null; + } + @Override public String toString() { return "UserEntity[userId=" + userId + ", name=" + name + ", email=" + email + "]"; @@ -69,7 +104,7 @@ public class UserEntity { user.setName(name); user.setUserId(userId); user.setProfilePicture(profilePicture); - user.setAlter(alter); + user.setGeburtsdatum(geburtsdatum); user.setGroesse(groesse); user.setGewicht(gewicht); user.setGeschlecht(geschlecht); diff --git a/xxxthegame/src/main/resources/static/aufgaben.html b/xxxthegame/src/main/resources/static/aufgaben.html index f332810..9e3c898 100644 --- a/xxxthegame/src/main/resources/static/aufgaben.html +++ b/xxxthegame/src/main/resources/static/aufgaben.html @@ -1,7 +1,7 @@ - + Aufgaben – XXX The Game @@ -608,44 +608,47 @@ resetSelection(); document.getElementById('userLoading').style.display = 'block'; fetch(`/gruppe/list/user?page=${userPage}&size=${PAGE_SIZE}`) - .then(r => r.json()) + .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then(data => { + console.log('[aufgaben] user gruppen:', data); userTotalPages = data.totalPages || 1; - renderGruppen('userList', data.content, 'user'); + try { renderGruppen('userList', data.content, 'user'); } catch(e) { console.error('[aufgaben] renderGruppen user Fehler:', e); throw e; } updatePaging('userPaging', 'userPrev', 'userNext', 'userPageInfo', userPage, userTotalPages); document.getElementById('userLoading').style.display = 'none'; reapplyPendingExpand(); }) - .catch(() => { document.getElementById('userLoading').textContent = 'Fehler beim Laden.'; }); + .catch(err => { console.error('[aufgaben] Fehler user gruppen:', err); document.getElementById('userLoading').textContent = 'Fehler beim Laden: ' + err.message; }); } function loadSystemGruppen() { resetSelection(); document.getElementById('systemLoading').style.display = 'block'; fetch(`/gruppe/list/system?page=${systemPage}&size=${PAGE_SIZE}`) - .then(r => r.json()) + .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then(data => { + console.log('[aufgaben] system gruppen:', data); systemTotalPages = data.totalPages || 1; - renderGruppen('systemList', data.content, 'system'); + try { renderGruppen('systemList', data.content, 'system'); } catch(e) { console.error('[aufgaben] renderGruppen system Fehler:', e); throw e; } updatePaging('systemPaging', 'systemPrev', 'systemNext', 'systemPageInfo', systemPage, systemTotalPages); document.getElementById('systemLoading').style.display = 'none'; reapplyPendingExpand(); }) - .catch(() => { document.getElementById('systemLoading').textContent = 'Fehler beim Laden.'; }); + .catch(err => { console.error('[aufgaben] Fehler system gruppen:', err); document.getElementById('systemLoading').textContent = 'Fehler beim Laden: ' + err.message; }); } function loadAboGruppen() { document.getElementById('aboLoading').style.display = 'block'; fetch(`/abo/list?page=${aboPage}&size=${PAGE_SIZE}`) - .then(r => r.json()) + .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then(data => { + console.log('[aufgaben] abo gruppen:', data); aboTotalPages = data.totalPages || 1; renderGruppen('aboList', data.content, 'abo'); updatePaging('aboPaging', 'aboPrev', 'aboNext', 'aboPageInfo', aboPage, aboTotalPages); document.getElementById('aboLoading').style.display = 'none'; reapplyPendingExpand(); }) - .catch(() => { document.getElementById('aboLoading').textContent = 'Fehler beim Laden.'; }); + .catch(err => { console.error('[aufgaben] Fehler abo gruppen:', err); document.getElementById('aboLoading').textContent = 'Fehler beim Laden: ' + err.message; }); } function reapplyPendingExpand() { diff --git a/xxxthegame/src/main/resources/static/bdsm-einladung.html b/xxxthegame/src/main/resources/static/bdsm-einladung.html new file mode 100644 index 0000000..6a13f43 --- /dev/null +++ b/xxxthegame/src/main/resources/static/bdsm-einladung.html @@ -0,0 +1,128 @@ + + + + + + + BDSM Game – Einladung – XXX The Game + + + + + +
+
+
Einladung wird geladen…
+ +
+
+ + + + diff --git a/xxxthegame/src/main/resources/static/sessionbdsm.html b/xxxthegame/src/main/resources/static/bdsm.html similarity index 95% rename from xxxthegame/src/main/resources/static/sessionbdsm.html rename to xxxthegame/src/main/resources/static/bdsm.html index 9ea92cc..2dea196 100644 --- a/xxxthegame/src/main/resources/static/sessionbdsm.html +++ b/xxxthegame/src/main/resources/static/bdsm.html @@ -1,14 +1,14 @@ - + BDSM Game – Neue Session – XXX The Game + + + + + +
+
+ +

BDSM Game

+

Schritt 2 von 4 – Mitspieler

+ +
+

Mitspieler

+
+ +
+ +
+
+ + +
+ +
+
+ + + + + diff --git a/xxxthegame/src/main/resources/static/sessionbdsmtasks.html b/xxxthegame/src/main/resources/static/bdsmtasks.html similarity index 93% rename from xxxthegame/src/main/resources/static/sessionbdsmtasks.html rename to xxxthegame/src/main/resources/static/bdsmtasks.html index b9af930..7075d55 100644 --- a/xxxthegame/src/main/resources/static/sessionbdsmtasks.html +++ b/xxxthegame/src/main/resources/static/bdsmtasks.html @@ -1,14 +1,14 @@ - + BDSM Game – Aufgaben-Gruppen – XXX The Game + + +
+
+
+
Warte auf Spielstart…
+
Der Host startet das Spiel in Kürze. Diese Seite aktualisiert sich automatisch.
+ + +
+
+ + + + diff --git a/xxxthegame/src/main/resources/static/benutzer.html b/xxxthegame/src/main/resources/static/benutzer.html index 88f2374..cff69e7 100644 --- a/xxxthegame/src/main/resources/static/benutzer.html +++ b/xxxthegame/src/main/resources/static/benutzer.html @@ -406,11 +406,11 @@ - +
- +
@@ -429,10 +429,10 @@
- -
-
- + +
+
+
@@ -467,6 +467,7 @@ // ── State ── const params = new URLSearchParams(window.location.search); let targetUserId = params.get('userId'); + const previewMode = params.get('preview'); // 'FREUND' | 'UNBEKANNT' | null let myUserId = null; let isOwnProfile = false; let profileData = null; @@ -524,25 +525,52 @@ } myUserId = me ? me.userId : null; - isOwnProfile = me && me.userId === profile.userId; + isOwnProfile = !previewMode && me && me.userId === profile.userId; profileData = profile; allImages = images; + // ── Preview-Modus: friendStatus simulieren ── + if (previewMode) { + profile.friendStatus = (previewMode === 'FREUND') ? 'FRIEND' : 'NONE'; + showPreviewBanner(previewMode); + } + + const isFriend = profile.friendStatus === 'FRIEND'; + document.title = profile.name + ' – XXX The Game'; renderHeader(profile); - renderGallery(); - loadFriends(); - if (profile.beschreibung) { + + // ── Galerie ── + if (canSee(profile.sichtbarkeitGalerie, isFriend, isOwnProfile)) { + renderGallery(); + } + + // ── Freunde ── + if (canSee(profile.sichtbarkeitFreunde, isFriend, isOwnProfile)) { + loadFriends(); + } + + if (profile.beschreibung && canSee(profile.sichtbarkeitGrunddaten, isFriend, isOwnProfile)) { document.getElementById('beschreibungLabel').style.display = ''; const el = document.getElementById('profilBeschreibung'); el.style.display = ''; el.textContent = profile.beschreibung; } - await loadPinnwand(); + + // ── Tabs: Feed, Pinnwand, Spielhistorie ── + applyTabPrivacy(profile, isFriend); + + if (canSee(profile.sichtbarkeitPinnwand, isFriend, isOwnProfile)) { + await loadPinnwand(); + } + document.getElementById('profileView').style.display = ''; - // Feed-Tab ist vorausgewählt → sofort laden - loadProfilPosts(); - profilPostsObserver.observe(document.getElementById('profilPostsSentinel')); + + // Feed-Tab ist vorausgewählt → sofort laden (nur wenn sichtbar) + if (canSee(profile.sichtbarkeitFeed, isFriend, isOwnProfile)) { + loadProfilPosts(); + profilPostsObserver.observe(document.getElementById('profilPostsSentinel')); + } } catch { document.getElementById('loadingHint').textContent = 'Fehler beim Laden.'; document.getElementById('loadingHint').style.display = ''; @@ -568,14 +596,16 @@ d.innerHTML = `${label}${esc(value)}`; tags.appendChild(d); }; - if (profile.alter) addTag('Alter', profile.alter + ' J.'); - if (profile.groesse) addTag('Größe', profile.groesse + ' cm'); - if (profile.gewicht) addTag('Gewicht', profile.gewicht + ' kg'); - if (profile.geschlecht) addTag('Geschlecht', GESCHLECHT_LABEL[profile.geschlecht] || profile.geschlecht); - if (profile.neigung) addTag('Neigung', NEIGUNG_LABEL[profile.neigung] || profile.neigung); - if (profile.beziehungsstatus) addTag('Beziehung', BEZIEHUNG_LABEL[profile.beziehungsstatus] || profile.beziehungsstatus); - if (profile.lockeeXp > 0) addTag('🔒 Lockee XP', profile.lockeeXp + ' XP'); - if (profile.keyholderXp > 0) addTag('🔑 Keyholder XP', profile.keyholderXp + ' XP'); + const grunddatenVisible = canSee(profile.sichtbarkeitGrunddaten, profile.friendStatus === 'FRIEND', isOwnProfile); + if (grunddatenVisible && profile.alter) addTag('Alter', profile.alter + ' J.'); + if (grunddatenVisible && profile.groesse) addTag('Größe', profile.groesse + ' cm'); + if (grunddatenVisible && profile.gewicht) addTag('Gewicht', profile.gewicht + ' kg'); + if (grunddatenVisible && profile.geschlecht) addTag('Geschlecht', GESCHLECHT_LABEL[profile.geschlecht] || profile.geschlecht); + if (grunddatenVisible && profile.neigung) addTag('Neigung', NEIGUNG_LABEL[profile.neigung] || profile.neigung); + if (grunddatenVisible && profile.beziehungsstatus) addTag('Beziehung', BEZIEHUNG_LABEL[profile.beziehungsstatus] || profile.beziehungsstatus); + const xpVisible = canSee(profile.sichtbarkeitXp, profile.friendStatus === 'FRIEND', isOwnProfile); + if (xpVisible && profile.lockeeXp > 0) addTag('🔒 Lockee XP', profile.lockeeXp + ' XP'); + if (xpVisible && profile.keyholderXp > 0) addTag('🔑 Keyholder XP', profile.keyholderXp + ' XP'); // Action buttons const actions = document.getElementById('profileActions'); @@ -596,6 +626,50 @@ } } + // ── Privacy helpers ── + function canSee(sichtbarkeit, isFriend, isOwn) { + if (isOwn || !sichtbarkeit) return true; + if (sichtbarkeit === 'ALLE') return true; + if (sichtbarkeit === 'NUR_FREUNDE') return isFriend; + // NUR_ICH + return false; + } + + function applyTabPrivacy(profile, isFriend) { + const showFeed = canSee(profile.sichtbarkeitFeed, isFriend, isOwnProfile); + const showPinnwand = canSee(profile.sichtbarkeitPinnwand, isFriend, isOwnProfile); + const showHistory = canSee(profile.sichtbarkeitLockhistorie, isFriend, isOwnProfile); + + const btnFeed = document.getElementById('tabBtnPosts'); + const btnPinnwand = document.getElementById('tabBtnPinnwand'); + const btnHistory = document.getElementById('tabBtnGameHistory'); + + if (!showFeed) { btnFeed.style.display = 'none'; document.getElementById('tab-posts').classList.remove('active'); } + if (!showPinnwand) { btnPinnwand.style.display = 'none'; } + if (!showHistory) { btnHistory.style.display = 'none'; } + + // Ersten sichtbaren Tab aktivieren + if (!showFeed) { + btnFeed.classList.remove('active'); + document.getElementById('tab-posts').classList.remove('active'); + if (showPinnwand) { + btnPinnwand.classList.add('active'); + document.getElementById('tab-pinnwand').classList.add('active'); + } else if (showHistory) { + btnHistory.classList.add('active'); + document.getElementById('tab-gamehistory').classList.add('active'); + } + } + } + + function showPreviewBanner(mode) { + const banner = document.createElement('div'); + banner.style.cssText = 'background:var(--color-secondary);border:1px solid var(--color-primary);border-radius:8px;padding:0.65rem 1rem;margin-bottom:1rem;font-size:0.88rem;display:flex;align-items:center;justify-content:space-between;gap:0.75rem;'; + const label = mode === 'FREUND' ? '👥 Vorschau aus Freundessicht' : '👤 Vorschau aus Sicht einer fremden Person'; + banner.innerHTML = `${label}← Einstellungen`; + document.getElementById('profileView').prepend(banner); + } + // ── Tab switching ── function switchProfilTab(name, btn) { document.querySelectorAll('.profil-tab-btn').forEach(b => b.classList.remove('active')); @@ -777,47 +851,60 @@ `; } - // ── Lock-Historie ── - let lockHistoryLoaded = false; - async function loadLockHistory() { - if (lockHistoryLoaded) return; - lockHistoryLoaded = true; - const list = document.getElementById('lockHistoryList'); - const empty = document.getElementById('lockHistoryEmpty'); + // ── Spielhistorie ── + const GAME_TYPE_ICON = { + CARDLOCK: '🔒', + TIMELOCK: '🔒', + BDSM: '⛓️', + VANILLA: '❤️' + }; + const ROLE_BADGE = { KEYHOLDER: '🔑', LOCKEE: '🔒', PLAYER: '' }; + + let gameHistoryLoaded = false; + async function loadGameHistory() { + if (gameHistoryLoaded) return; + gameHistoryLoaded = true; + const list = document.getElementById('gameHistoryList'); + const empty = document.getElementById('gameHistoryEmpty'); list.innerHTML = '

Lädt…

'; try { - const res = await fetch('/lockhistory?userId=' + targetUserId); + const res = await fetch('/gamehistory?userId=' + targetUserId); if (!res.ok) { list.innerHTML = ''; return; } const entries = await res.json(); list.innerHTML = ''; if (entries.length === 0) { empty.style.display = ''; return; } + list.innerHTML = entries.map(e => { - const icon = e.role === 'KEYHOLDER' ? '🔑' : '🔒'; - const partner = e.role === 'KEYHOLDER' - ? (e.lockeeName ? `Lockee: ${esc(e.lockeeName)}` : '') - : (e.keyholderName ? `Keyholder: ${esc(e.keyholderName)}` : 'Self-Lock'); - const days = Math.floor(e.durationMinutes / 1440); - const hours = Math.floor((e.durationMinutes % 1440) / 60); - const mins = e.durationMinutes % 60; - const dur = days > 0 + const gameIconRaw = GAME_TYPE_ICON[e.gameType] || '🎮'; + const gameIcon = (e.gameType === 'CARDLOCK' || e.gameType === 'TIMELOCK') + ? gameIconRaw + : `${gameIconRaw}`; + + const days = Math.floor(e.durationMinutes / 1440); + const hours = Math.floor((e.durationMinutes % 1440) / 60); + const mins = e.durationMinutes % 60; + const dur = days > 0 ? `${days}d ${hours}h ${mins}min` : hours > 0 ? `${hours}h ${mins}min` : `${mins}min`; - const avatar = e.partnerPic - ? `` - : `
👤
`; - return `
-
- ${avatar} - ${icon} -
+ + const participants = (e.participants || []).map(p => { + const badge = ROLE_BADGE[p.role] || ''; + const img = p.picture + ? `` + : `
👤
`; + return ` + ${img} + ${badge ? `${badge}` : ''} + `; + }).join(''); + + return `
+
${gameIcon}
-
${esc(e.lockName) || 'Unbenanntes Lock'}
-
- ${partner} - ⏱ ${dur} - ${fmtDate(e.unlockTime)} -
+
${esc(e.gameName) || 'Unbenannt'}
+
⏱ ${dur}  ·  ${new Date(e.endTime).toLocaleDateString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric'})}
+
${participants}
`; }).join(''); } catch(e) { list.innerHTML = ''; } @@ -954,7 +1041,7 @@ : '◉'; const bildRaw = bilderCarousel(p.bilder); const bildHtml = bildRaw - ? `
${bildRaw}
💬
` + ? `
${bildRaw}
` : ''; const privacyLabel = p.isPublic ? '' : '🔒 privat'; diff --git a/xxxthegame/src/main/resources/static/einladungen.html b/xxxthegame/src/main/resources/static/einladungen.html index bd7e665..c5d3054 100644 --- a/xxxthegame/src/main/resources/static/einladungen.html +++ b/xxxthegame/src/main/resources/static/einladungen.html @@ -167,6 +167,33 @@ } .blind-hint-icon { font-size: 1.4rem; flex-shrink: 0; } + /* Bestätigungs-Modal */ + .confirm-modal-bg { + display: none; position: fixed; inset: 0; z-index: 600; + align-items: center; justify-content: center; + } + .confirm-modal-bg.open { display: flex; } + .confirm-modal-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.6); } + .confirm-modal-box { + position: relative; background: var(--color-card); + border: 1px solid var(--color-secondary); border-radius: 12px; + padding: 1.75rem 1.5rem 1.5rem; max-width: 380px; width: 92%; z-index: 1; + display: flex; flex-direction: column; gap: 1rem; + } + .confirm-modal-title { + font-weight: 700; font-size: 1rem; padding-right: 1.5rem; + } + .confirm-modal-text { + font-size: 0.9rem; color: var(--color-muted); line-height: 1.5; + } + .confirm-modal-actions { + display: flex; gap: 0.6rem; justify-content: flex-end; margin-top: 0.25rem; + } + .confirm-modal-actions button { width: auto; padding: 0.6rem 1.3rem; font-size: 0.9rem; } + .confirm-modal-cancel { background: var(--color-secondary) !important; color: var(--color-text) !important; } + .confirm-modal-ok { background: #c0392b !important; } + .confirm-modal-ok:hover { background: #a93226 !important; } + /* Entsperrcode-Modal */ .unlock-modal-bg { display: none; position: fixed; inset: 0; z-index: 500; @@ -214,6 +241,20 @@
+ +
+
+
+ +
+
+
+ + +
+
+
+
@@ -475,9 +516,29 @@ renderSentPage(); } + // ── Bestätigungs-Modal ── + let _confirmResolve = null; + + function showConfirm(title, text) { + document.getElementById('confirmTitle').textContent = title; + document.getElementById('confirmText').textContent = text; + document.getElementById('confirmModal').classList.add('open'); + return new Promise(resolve => { + _confirmResolve = resolve; + document.getElementById('confirmOkBtn').onclick = () => { confirmClose(true); }; + }); + } + + function confirmCancel() { confirmClose(false); } + + function confirmClose(result) { + document.getElementById('confirmModal').classList.remove('open'); + if (_confirmResolve) { _confirmResolve(result); _confirmResolve = null; } + } + // ── Aktionen: Empfangen ── async function declineLockeeInvitation(token, btn) { - if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return; + if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return; btn.disabled = true; try { const res = await fetch('/lockee/invitation/' + encodeURIComponent(token), { method: 'DELETE' }); @@ -487,7 +548,7 @@ } async function declineKhInvitation(token, btn) { - if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return; + if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest?')) return; btn.disabled = true; try { const res = await fetch('/keyholder/invitations/mine/' + encodeURIComponent(token), { method: 'DELETE' }); @@ -498,10 +559,11 @@ // ── Aktionen: Gesendet ── async function cancelSentInvitation(token, type, btn) { - const msg = type === 'lockee' - ? 'Einladung zurückziehen? Das Lock wird gelöscht und der Lockee wird benachrichtigt.' - : 'Keyholder-Einladung zurückziehen? Der Keyholder wird benachrichtigt.'; - if (!confirm(msg)) return; + const title = 'Einladung zurückziehen'; + const text = type === 'lockee' + ? 'Das Lock wird gelöscht und der Lockee wird benachrichtigt.' + : 'Der Keyholder wird benachrichtigt.'; + if (!await showConfirm(title, text)) return; btn.disabled = true; const url = type === 'lockee' ? '/lockee/invitations/sent/' + encodeURIComponent(token) @@ -634,7 +696,7 @@ async function declineLockeeInviteDialog() { if (!activeDialogToken) return; - if (!confirm('Bist du sicher, dass du diese Einladung ablehnen möchtest? Das Lock wird gelöscht.')) return; + if (!await showConfirm('Einladung ablehnen', 'Bist du sicher, dass du diese Einladung ablehnen möchtest? Das Lock wird gelöscht.')) return; const declineBtn = document.querySelector('.btn-decline'); declineBtn.disabled = true; try { diff --git a/xxxthegame/src/main/resources/static/einstellungen.html b/xxxthegame/src/main/resources/static/einstellungen.html new file mode 100644 index 0000000..f2943f1 --- /dev/null +++ b/xxxthegame/src/main/resources/static/einstellungen.html @@ -0,0 +1,968 @@ + + + + + + + Einstellungen – XXX The Game + + + + + +
+
+

⚙️ Einstellungen

+ + +
+
+ 👤 Grunddaten + +
+
+ +
+
+
Nickname
+
+
+ +
+ +
+
+
E-Mail
+
+
+ +
+ +
+
+
Geburtsdatum
+
+
+ +
+ +
+
+
Konto löschen
+
Alle Daten werden unwiderruflich gelöscht
+
+ +
+ +
+
+ + +
+
+ 🕹️ Spiel Einstellungen + +
+
+ + +
+
BDSM Game
+ +
+
Spiele mit Geschlecht
+
+ + + +
+
+ +
+
Meine Rollen
+
+ + + + +
+
+ +
+
Was ich einsetze
+
+ + + + + +
+
+
+ +
+
+ + +
+
+ 🔔 Benachrichtigungen + +
+
+
+ + +
+
+
In-App
+
E-Mail
+
+ + +
+
+
Einladungen
+
Einladungen zu Locks und Spielen, Annahmen und Ablehnungen
+
+
+ +
+
+ +
+
+ + +
+
+
Spielstatus
+
Karten, Aufgaben, Verifikationen, Einfrierungen und andere Spielereignisse
+
+
+ +
+
+ +
+
+ + +
+
+
Notfall
+
Notfall-Entsperrungen und dringende Meldungen
+
+
+ +
+
+ +
+
+ + +
+
+
Freundschaftsanfragen
+
Neue Freundschaftsanfragen von anderen Nutzern
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ 🛡️ Datenschutz + +
+
+ + +
+
+
Grunddaten
+
Alter, Größe, Gewicht, Geschlecht, Neigung, Beziehungsstatus, Beschreibung
+
+ +
+ + +
+
+
Galerie
+
Fotos auf dem Profil
+
+ +
+ + +
+
+
Freundesliste
+
Wer kann sehen, wer deine Freunde sind
+
+ +
+ + +
+
+
Feed / Posts
+
Posts auf dem Profil-Tab
+
+ +
+ + +
+
+
Pinnwand
+
Einträge auf der Pinnwand
+
+ +
+ + +
+
+
XP-Punkte
+
Lockee-XP und Keyholder-XP
+
+ +
+ + +
+
+
Lock-Historie
+
Abgeschlossene Locks und Keyholder-Aktivitäten
+
+ +
+ +
+ + +
+ Profil-Vorschau – wie sieht mein Profil für andere aus? + + +
+ +
+
+
+
+ + + + + + + + + + + + + +
✓ Gespeichert
+ + + + + + diff --git a/xxxthegame/src/main/resources/static/js/sidebar.js b/xxxthegame/src/main/resources/static/js/sidebar.js index 1b15c1b..bf99bbd 100644 --- a/xxxthegame/src/main/resources/static/js/sidebar.js +++ b/xxxthegame/src/main/resources/static/js/sidebar.js @@ -13,8 +13,8 @@ label: 'BDSM Game', icon: '◆', items: [ - { href: '/sessionbdsm.html', icon: '▷', label: 'Neue Session', id: 'navBdsmNeu' }, - { href: '/sessionbdsmingame.html', icon: '▶', label: 'Im Spiel', id: 'navBdsmImSpiel' }, + { href: '/bdsm.html', icon: '▷', label: 'Neue Session', id: 'navBdsmNeu' }, + { href: '/bdsmingame.html', icon: '▶', label: 'Im Spiel', id: 'navBdsmImSpiel' }, { href: '/aufgaben.html', icon: '✓', label: 'Aufgaben' }, { href: '/toys.html', icon: '◈', label: 'Toys' }, { href: '/entdecken.html', icon: '⊙', label: 'Entdecken' }, @@ -105,7 +105,7 @@ // BDSM Session-Status try { - const sessionRes = await fetch(`/session?userId=${user.userId}`); + const sessionRes = await fetch(`/bdsm?userId=${user.userId}`); const hasSession = sessionRes.status === 200; if (navNeu) navNeu.style.display = hasSession ? 'none' : ''; if (navImSpiel) navImSpiel.style.display = hasSession ? '' : 'none'; diff --git a/xxxthegame/src/main/resources/static/js/social-sidebar.js b/xxxthegame/src/main/resources/static/js/social-sidebar.js index 841b9b0..e054108 100644 --- a/xxxthegame/src/main/resources/static/js/social-sidebar.js +++ b/xxxthegame/src/main/resources/static/js/social-sidebar.js @@ -39,6 +39,7 @@

  • ${desktopItems}

  • +
  • ⚙️ Einstellungen
  • Abmelden
  • `; @@ -66,11 +67,12 @@ `; const sep = ''; + const mobileSettings = ``; const logoutLi = sidebarUl.querySelector('a[href="/login/logout"]')?.closest('li'); if (logoutLi) { - logoutLi.insertAdjacentHTML('beforebegin', sep + mobileLinks + mobileProfile); + logoutLi.insertAdjacentHTML('beforebegin', sep + mobileLinks + mobileProfile + mobileSettings); } else { - sidebarUl.insertAdjacentHTML('beforeend', sep + mobileLinks + mobileProfile); + sidebarUl.insertAdjacentHTML('beforeend', sep + mobileLinks + mobileProfile + mobileSettings); } } diff --git a/xxxthegame/src/main/resources/static/profile.html b/xxxthegame/src/main/resources/static/profile.html index 7b085a1..83b9965 100644 --- a/xxxthegame/src/main/resources/static/profile.html +++ b/xxxthegame/src/main/resources/static/profile.html @@ -1,7 +1,7 @@ - + Profil – XXX The Game @@ -269,35 +269,19 @@
    -
    +
    -
    - -
    -

    - -
    -
    - -
    - -
    -

    - -
    -
    -
    - +
    @@ -346,10 +330,6 @@
    -
    - -
    -
    @@ -364,50 +344,6 @@
    - - - - - - - - - @@ -424,14 +360,18 @@ }) .then(user => { if (!user) return; - document.getElementById('userName').textContent = user.name; - document.getElementById('userEmail').textContent = user.email; if (user.profilePicture) { currentPicture = user.profilePicture; renderPicture(currentPicture); } // Fill optional profile fields - if (user.alter) document.getElementById('profileAlter').value = user.alter; + if (user.geburtsdatum) { + const birth = new Date(user.geburtsdatum); + const today = new Date(); + const age = today.getFullYear() - birth.getFullYear() + - (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0); + document.getElementById('profileAlter').value = age + ' Jahre'; + } if (user.groesse) document.getElementById('profileGroesse').value = user.groesse; if (user.gewicht) document.getElementById('profileGewicht').value = user.gewicht; if (user.geschlecht) document.getElementById('profileGeschlecht').value = user.geschlecht; @@ -506,7 +446,6 @@ method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - alter: toNullOrInt('profileAlter'), groesse: toNullOrInt('profileGroesse'), gewicht: toNullOrInt('profileGewicht'), geschlecht: toNullOrStr('profileGeschlecht'), @@ -530,105 +469,6 @@ } } - // ── Nickname dialog ── - function openNameDialog() { - document.getElementById('newName').value = ''; - hideModalMessage('nameMessage'); - document.getElementById('nameModal').classList.add('visible'); - document.getElementById('newName').focus(); - } - - function closeNameDialog() { - document.getElementById('nameModal').classList.remove('visible'); - } - - async function saveName() { - const newName = document.getElementById('newName').value.trim(); - if (!newName) { - showModalMessage('nameMessage', 'Bitte einen Namen eingeben.', 'error'); - return; - } - const btn = document.getElementById('nameConfirmBtn'); - btn.disabled = true; - btn.textContent = 'Wird gespeichert…'; - hideModalMessage('nameMessage'); - - try { - const response = await fetch('/user/me/name', { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name: newName }) - }); - if (response.ok) { - document.getElementById('userName').textContent = newName; - closeNameDialog(); - showMessage('Nickname geändert.', 'success'); - } else if (response.status === 409) { - showModalMessage('nameMessage', 'Dieser Nickname ist bereits vergeben.', 'error'); - } else { - showModalMessage('nameMessage', `Fehler: HTTP ${response.status}`, 'error'); - } - } catch (err) { - showModalMessage('nameMessage', 'Server nicht erreichbar.', 'error'); - console.error(err); - } finally { - btn.disabled = false; - btn.textContent = 'Speichern'; - } - } - - // ── E-Mail dialog ── - function openEmailDialog() { - document.getElementById('newEmail').value = ''; - hideModalMessage('emailMessage'); - document.getElementById('emailModal').classList.add('visible'); - document.getElementById('newEmail').focus(); - } - - function closeEmailDialog() { - document.getElementById('emailModal').classList.remove('visible'); - } - - async function requestEmailChange() { - const newEmail = document.getElementById('newEmail').value.trim(); - if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) { - showModalMessage('emailMessage', 'Bitte eine gültige E-Mail-Adresse eingeben.', 'error'); - return; - } - const btn = document.getElementById('emailConfirmBtn'); - btn.disabled = true; - btn.textContent = 'Wird gesendet…'; - hideModalMessage('emailMessage'); - - try { - const response = await fetch('/email-change', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ newEmail }) - }); - if (response.status === 202) { - showModalMessage('emailMessage', - 'Bestätigungsmail wurde an die neue Adresse gesendet. Bitte bestätige die Änderung über den Link in der E-Mail.', - 'success'); - btn.disabled = true; - btn.textContent = 'Gesendet'; - } else if (response.status === 409) { - showModalMessage('emailMessage', 'Diese E-Mail-Adresse ist bereits vergeben.', 'error'); - btn.disabled = false; - btn.textContent = 'Bestätigungsmail senden'; - } else { - showModalMessage('emailMessage', `Fehler: HTTP ${response.status}`, 'error'); - btn.disabled = false; - btn.textContent = 'Bestätigungsmail senden'; - } - } catch (err) { - showModalMessage('emailMessage', 'Server nicht erreichbar.', 'error'); - btn.disabled = false; - btn.textContent = 'Bestätigungsmail senden'; - console.error(err); - } - } - // ── Gallery ── let myUserId = null; @@ -755,71 +595,6 @@ function hideMessage() { document.getElementById('message').style.display = 'none'; } - - function showModalMessage(id, text, type) { - const el = document.getElementById(id); - el.textContent = text; - el.className = `message ${type}`; - el.style.display = 'block'; - } - - function hideModalMessage(id) { - document.getElementById(id).style.display = 'none'; - } - - // ── Konto löschen ── - function openDeleteDialog() { - hideModalMessage('deleteMessage'); - document.getElementById('deleteConfirmBtn').disabled = false; - document.getElementById('deleteConfirmBtn').textContent = 'Konto löschen'; - document.getElementById('deleteModal').classList.add('visible'); - } - - function closeDeleteDialog() { - document.getElementById('deleteModal').classList.remove('visible'); - } - - async function deleteAccount() { - const btn = document.getElementById('deleteConfirmBtn'); - btn.disabled = true; - btn.textContent = 'Wird gelöscht…'; - hideModalMessage('deleteMessage'); - - try { - const response = await fetch('/user/me', { method: 'DELETE' }); - if (response.ok) { - window.location.href = '/login.html?accountDeleted=1'; - } else { - showModalMessage('deleteMessage', `Fehler: HTTP ${response.status}`, 'error'); - btn.disabled = false; - btn.textContent = 'Konto löschen'; - } - } catch (err) { - showModalMessage('deleteMessage', 'Server nicht erreichbar.', 'error'); - btn.disabled = false; - btn.textContent = 'Konto löschen'; - console.error(err); - } - } - - // Close modals on backdrop click - document.getElementById('deleteModal').addEventListener('click', e => { - if (e.target === document.getElementById('deleteModal')) closeDeleteDialog(); - }); - document.getElementById('nameModal').addEventListener('click', e => { - if (e.target === document.getElementById('nameModal')) closeNameDialog(); - }); - document.getElementById('emailModal').addEventListener('click', e => { - if (e.target === document.getElementById('emailModal')) closeEmailDialog(); - }); - - // Enter key in modal inputs - document.getElementById('newName').addEventListener('keydown', e => { - if (e.key === 'Enter') saveName(); - }); - document.getElementById('newEmail').addEventListener('keydown', e => { - if (e.key === 'Enter') requestEmailChange(); - }); diff --git a/xxxthegame/src/main/resources/static/registration.html b/xxxthegame/src/main/resources/static/registration.html index af529b1..de4c9df 100644 --- a/xxxthegame/src/main/resources/static/registration.html +++ b/xxxthegame/src/main/resources/static/registration.html @@ -1,7 +1,7 @@ - + xXx Games – Neues Konto erstellen @@ -19,6 +19,9 @@ + + + @@ -50,11 +53,12 @@ async function register() { const name = document.getElementById('name').value.trim(); const email = document.getElementById('email').value.trim(); + const geburtsdatum = document.getElementById('geburtsdatum').value; const password = document.getElementById('password').value; const passwordConfirm = document.getElementById('passwordConfirm').value; const btn = document.getElementById('registerBtn'); - if (!name || !email || !password || !passwordConfirm) { + if (!name || !email || !geburtsdatum || !password || !passwordConfirm) { showMessage('Bitte alle Felder ausfüllen.', 'error'); return; } @@ -62,6 +66,14 @@ showMessage('Bitte eine gültige E-Mail-Adresse eingeben.', 'error'); return; } + const today = new Date(); + const birth = new Date(geburtsdatum); + const age = today.getFullYear() - birth.getFullYear() + - (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0); + if (age < 18) { + showMessage('Du musst mindestens 18 Jahre alt sein, um dich zu registrieren.', 'error'); + return; + } if (password !== passwordConfirm) { showMessage('Die Passwörter stimmen nicht überein.', 'error'); return; @@ -76,11 +88,15 @@ const response = await fetch('/registration', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name, email, passwordHash }) + body: JSON.stringify({ name, email, passwordHash, geburtsdatum }) }); if (response.status === 202) { window.location.href = `/activate.html?email=${encodeURIComponent(email)}`; + } else if (response.status === 422) { + showMessage('Du musst mindestens 18 Jahre alt sein, um dich zu registrieren.', 'error'); + btn.disabled = false; + btn.textContent = 'Registrieren'; } else if (response.status === 400) { showMessage('Diese E-Mail-Adresse ist bereits registriert.', 'error'); btn.disabled = false; diff --git a/xxxthegame/src/main/resources/static/sessionbdsmplayers.html b/xxxthegame/src/main/resources/static/sessionbdsmplayers.html deleted file mode 100644 index ba3f2e6..0000000 --- a/xxxthegame/src/main/resources/static/sessionbdsmplayers.html +++ /dev/null @@ -1,439 +0,0 @@ - - - - - - - BDSM Game – Mitspieler – XXX The Game - - - - - -
    -
    - -

    BDSM Game

    -

    Schritt 2 von 4 – Mitspieler

    - -
    -

    Mitspieler

    -
    - -
    - -
    -
    - - -
    - -
    -
    - - - - - diff --git a/xxxthegame/src/main/resources/static/userhome.html b/xxxthegame/src/main/resources/static/userhome.html index 58c7f52..9c399f5 100644 --- a/xxxthegame/src/main/resources/static/userhome.html +++ b/xxxthegame/src/main/resources/static/userhome.html @@ -54,7 +54,7 @@ Tauche ein in strukturierte Sessions mit Aufgaben, Toys und klaren Rollen. Definiere Grenzen, vergib Aufgaben und erlebe intensive Momente mit deinem Partner.

    - +