Änderungen am Msessage System, Datenschutz-Einstellungen hinzugefügt, BDSM und CardLock Game weiterverfeinert
This commit is contained in:
@@ -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:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
970
.metadata/.log
970
.metadata/.log
@@ -1097,3 +1097,973 @@ Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(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.<init>(FileInputStream.java:152)
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(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.<init>(FileInputStream.java:152)
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(FileInputStream.java:152)
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(FileInputStream.java:152)
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(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.<init>(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.<init>(FileInputStream.java:152)
|
||||
at java.base/java.io.FileInputStream.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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.<init>(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)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -18,4 +18,5 @@
|
||||
<fullyQualifiedTypeName name="lombok.Getter"/>
|
||||
<fullyQualifiedTypeName name="lombok.Setter"/>
|
||||
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.cardlock.Test"/>
|
||||
<fullyQualifiedTypeName name="de.oaa.xxx.games.chastity.KeyholderCardLock"/>
|
||||
</qualifiedTypeNameHistroy>
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
<section name="RenameInformationPopup">
|
||||
</section>
|
||||
<section name="org.eclipse.ltk.ui.refactoring.settings">
|
||||
<item key="renameSubpackages" value="false"/>
|
||||
<item key="updateTextualMatches" value="false"/>
|
||||
<item key="updateQualifiedNames" value="false"/>
|
||||
<item key="patterns" value="*"/>
|
||||
</section>
|
||||
<section name="org.eclipse.jdt.internal.ui.dialogs.OpenTypeSelectionDialog2">
|
||||
<item key="ShowStatusLine" value="true"/>
|
||||
@@ -68,4 +72,13 @@
|
||||
<section name="BuildPathsPropertyPage">
|
||||
<item key="pageIndex" value="3"/>
|
||||
</section>
|
||||
<section name="org.eclipse.jdt.internal.ui.typehierarchy.QuickHierarchy">
|
||||
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_WIDTH" value="400"/>
|
||||
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_HEIGHT" value="346"/>
|
||||
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_USE_PERSISTED_SIZE" value="true"/>
|
||||
<item key="org.eclipse.jdt.internal.ui.typehierarchy.HierarchyInformationControlDIALOG_USE_PERSISTED_LOCATION" value="false"/>
|
||||
</section>
|
||||
<section name="NewPackageWizardPage">
|
||||
<item key="create_package_info_java" value="false"/>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<item key="DIALOG_X_ORIGIN" value="20"/>
|
||||
<item key="DIALOG_Y_ORIGIN" value="20"/>
|
||||
<item key="DIALOG_WIDTH" value="874"/>
|
||||
<item key="DIALOG_HEIGHT" value="995"/>
|
||||
<item key="DIALOG_HEIGHT" value="1050"/>
|
||||
<item key="DIALOG_FONT_NAME" value="1|Ubuntu Sans|11.0|0|GTK|1|"/>
|
||||
</section>
|
||||
<section name="ImportExportAction">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.UUID;
|
||||
@Getter
|
||||
@Setter
|
||||
@Entity
|
||||
@Table(name = "aufgabenGruppe")
|
||||
@Table(name = "aufgaben_gruppe")
|
||||
public class AufgabenGruppeEntity {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -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) + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
public enum AufgabeArt {
|
||||
AUFGABE,
|
||||
@@ -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;
|
||||
@@ -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> mitspieler = new ArrayList<>();
|
||||
private final List<AktiveSperre> 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<AufgabeAnzeige> getFinisher() {
|
||||
var list = new ArrayList<AufgabeAnzeige>();
|
||||
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<Aufgabe> 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<Strafe> 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<Sperre> 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<Mitspieler> 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<Mitspieler> 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> mitspieler = new ArrayList<>();
|
||||
private final List<AktiveSperre> 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<AufgabeAnzeige> getFinisher() {
|
||||
var list = new ArrayList<AufgabeAnzeige>();
|
||||
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<Aufgabe> 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<Strafe> 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<Sperre> 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<Mitspieler> 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<Mitspieler> 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;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
public enum GeschlechtEnum {
|
||||
WEIBLICH,
|
||||
@@ -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<GeschlechtEnum> spieltMit;
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
public enum RolleEnum {
|
||||
BESTRAFUNG_AKTIV,
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session;
|
||||
package de.oaa.xxx.games.bdsm;
|
||||
|
||||
public enum Werkzeug {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package de.oaa.xxx.session.aufgaben;
|
||||
package de.oaa.xxx.games.bdsm.aufgaben;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Map<String, Object>> 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<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("einladungId", entity.getEinladungId());
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> 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<List<Map<String, Object>>> getBySetupId(@RequestParam UUID setupId, Principal principal) {
|
||||
UUID userId = currentUserId(principal);
|
||||
if (userId == null) return ResponseEntity.status(401).build();
|
||||
List<Map<String, Object>> list = einladungRepository.findBySetupId(setupId).stream()
|
||||
.map(this::toMap).toList();
|
||||
return ResponseEntity.ok(list);
|
||||
}
|
||||
|
||||
@GetMapping("/pending")
|
||||
public ResponseEntity<List<Map<String, Object>>> getPending(Principal principal) {
|
||||
UUID userId = currentUserId(principal);
|
||||
if (userId == null) return ResponseEntity.status(401).build();
|
||||
List<Map<String, Object>> list = einladungRepository.findByInviteeIdAndStatus(userId, Status.PENDING)
|
||||
.stream().map(e -> {
|
||||
Map<String, Object> 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<List<Map<String, Object>>> getSent(Principal principal) {
|
||||
UUID userId = currentUserId(principal);
|
||||
if (userId == null) return ResponseEntity.status(401).build();
|
||||
List<Map<String, Object>> list = einladungRepository.findByInviterIdAndStatus(userId, Status.PENDING)
|
||||
.stream().map(e -> {
|
||||
Map<String, Object> 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<Map<String, Object>> 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<String, Object> 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<Void> 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<String, Object> toMap(BdsmEinladungEntity e) {
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<Session> getBySessionId(@PathVariable UUID sessionId) {
|
||||
public ResponseEntity<BdsmGame> getBySessionId(@PathVariable UUID sessionId) {
|
||||
return sessionRepository.findById(sessionId)
|
||||
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
||||
.orElse(ResponseEntity.noContent().build());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<Session> getByUserId(@RequestParam UUID userId) {
|
||||
public ResponseEntity<BdsmGame> getByUserId(@RequestParam UUID userId) {
|
||||
return sessionRepository.findByUserId(userId)
|
||||
.map(entity -> ResponseEntity.ok(toSession(entity)))
|
||||
.orElse(ResponseEntity.noContent().build());
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<Void> create(@RequestBody Session session) {
|
||||
public ResponseEntity<Void> 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<Void> deleteSession(@RequestBody Session session) {
|
||||
public ResponseEntity<Void> 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<AufgabeAnzeige> 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<List<AufgabeAnzeige>> 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<Void> 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<Map<String, Object>> 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<String, Object> 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<Void> 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<Void> 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<ActiveTaskResponse> 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());
|
||||
@@ -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<List<AktiveSperre>> 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<Mitspieler> mitspielerList = session.getMitspieler().stream()
|
||||
.map(m -> m.toMitspieler())
|
||||
@@ -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<Mitspieler> mitspielerList) {
|
||||
AktiveSperre sperre = new AktiveSperre();
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 + "]";
|
||||
@@ -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<Werkzeug> 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<GeschlechtEnum> 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<RolleEnum> rollen = new ArrayList<>();
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "sessionId", nullable = false)
|
||||
private SessionEntity session;
|
||||
@OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER)
|
||||
private List<AktiveSperreEntity> 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<Werkzeug> 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<GeschlechtEnum> 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<RolleEnum> rollen = new ArrayList<>();
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "sessionId", nullable = false)
|
||||
private BdsmGameEntity session;
|
||||
@OneToMany(mappedBy = "mitspieler", fetch = FetchType.EAGER)
|
||||
private List<AktiveSperreEntity> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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<BdsmDefaultsEntity, UUID> {
|
||||
Optional<BdsmDefaultsEntity> findByUserId(UUID userId);
|
||||
}
|
||||
@@ -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<BdsmEinladungEntity, UUID> {
|
||||
|
||||
List<BdsmEinladungEntity> findBySetupId(UUID setupId);
|
||||
|
||||
List<BdsmEinladungEntity> findByInviteeIdAndStatus(UUID inviteeId, Status status);
|
||||
|
||||
List<BdsmEinladungEntity> findByInviterIdAndStatus(UUID inviterId, Status status);
|
||||
}
|
||||
@@ -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<BdsmGameEntity, UUID> {
|
||||
|
||||
Optional<BdsmGameEntity> findByUserId(UUID userId);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<LocalDate> 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<LocalDate> 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 -> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<List<Map<String, Object>>> 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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<LockHistoryEntity, UUID> {
|
||||
|
||||
List<LockHistoryEntity> findByUserIdOrderByEndTimeDesc(UUID userId);
|
||||
|
||||
}
|
||||
@@ -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<List<Map<String, Object>>> 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<String, Object> 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<Map<String, Object>> participants = e.getParticipants().stream()
|
||||
.map(p -> {
|
||||
Map<String, Object> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<ParticipantDTO> participants
|
||||
) {
|
||||
public record ParticipantDTO(UUID userId, GameRole role) {}
|
||||
}
|
||||
@@ -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<GameHistoryParticipantEntity> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<GameHistoryParticipantEntity, UUID> {
|
||||
}
|
||||
@@ -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<GameHistoryEntity, UUID> {
|
||||
|
||||
@Query("SELECT DISTINCT h FROM GameHistoryEntity h JOIN h.participants p WHERE p.userId = :userId ORDER BY h.endTime DESC")
|
||||
List<GameHistoryEntity> 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<GameHistoryEntity> findByParticipantUserIdAndGameType(@Param("userId") UUID userId, @Param("gameType") GameType gameType);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.oaa.xxx.games.history;
|
||||
|
||||
public enum GameRole {
|
||||
LOCKEE,
|
||||
KEYHOLDER,
|
||||
PLAYER
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.oaa.xxx.games.history;
|
||||
|
||||
public enum GameType {
|
||||
CARDLOCK,
|
||||
TIMELOCK,
|
||||
BDSM,
|
||||
VANILLA
|
||||
}
|
||||
@@ -165,6 +165,56 @@ public class MailTemplateService {
|
||||
);
|
||||
}
|
||||
|
||||
public String buildNotificationMail(String name, String text, String targetUrl, String baseUrl) {
|
||||
String actionButton = targetUrl != null
|
||||
? """
|
||||
<div style="text-align:center; margin:0 0 2rem 0;">
|
||||
<a href="%s%s"
|
||||
style="display:inline-block; padding:0.75rem 2.5rem; background:%s; color:#ffffff;
|
||||
border-radius:6px; text-decoration:none; font-weight:600; font-size:1rem;">
|
||||
Zur Anwendung
|
||||
</a>
|
||||
</div>
|
||||
""".formatted(baseUrl, targetUrl, colorPrimary)
|
||||
: "<div style=\"margin:0 0 2rem 0;\"></div>";
|
||||
|
||||
String settingsUrl = baseUrl + "/einstellungen.html#sec-benachrichtigungen";
|
||||
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<body style="margin:0; padding:2rem; background:%s; font-family:'Segoe UI',Arial,sans-serif; color:%s;">
|
||||
<div style="max-width:460px; margin:0 auto; background:%s; border:1px solid %s; border-radius:12px; padding:2.5rem; box-shadow:0 8px 32px rgba(0,0,0,0.5);">
|
||||
|
||||
<h1 style="color:%s; text-align:center; margin:0 0 1.5rem 0; font-size:1.6rem;">XXX The Game</h1>
|
||||
|
||||
<p style="color:%s; margin:0 0 0.75rem 0;">Hallo %s,</p>
|
||||
<p style="color:%s; margin:0 0 2rem 0;">%s</p>
|
||||
|
||||
%s
|
||||
|
||||
<hr style="border:none; border-top:1px solid %s; margin:0 0 1.5rem 0;">
|
||||
|
||||
<p style="color:%s; font-size:0.85em; margin:0;">
|
||||
Du erhältst diese E-Mail, weil du E-Mail-Benachrichtigungen für diese Kategorie aktiviert hast.
|
||||
Du kannst deine Einstellungen jederzeit unter
|
||||
<a href="%s" style="color:%s;">Einstellungen → Benachrichtigungen</a> anpassen.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""".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 """
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -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<Void> 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<Void> response = userController.userAnlegen(reg);
|
||||
ResponseEntity<Void> response = userController.userAnlegen(registration.toRegistration());
|
||||
if (response.getStatusCode().is2xxSuccessful()) {
|
||||
registration.setActivated(Boolean.TRUE);
|
||||
registrationRepository.save(registration);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<String> 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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<SessionEntity, UUID> {
|
||||
|
||||
Optional<SessionEntity> findByUserId(UUID userId);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.oaa.xxx.social.entity;
|
||||
|
||||
public enum MessageCause {
|
||||
INVITATION,
|
||||
GAME_STATE,
|
||||
EMERGENCY,
|
||||
FRIENDREQUEST
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<NotificationPreferenceEntity, UUID> {
|
||||
|
||||
List<NotificationPreferenceEntity> findByUserId(UUID userId);
|
||||
|
||||
Optional<NotificationPreferenceEntity> findByUserIdAndCause(UUID userId, MessageCause cause);
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.oaa.xxx.user;
|
||||
|
||||
public enum Sichtbarkeit {
|
||||
ALLE,
|
||||
NUR_FREUNDE,
|
||||
NUR_ICH
|
||||
}
|
||||
@@ -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 + "]";
|
||||
|
||||
@@ -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<Void> 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<Void> 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<Map<String, Object>> getNotifications(Principal principal) {
|
||||
var userOpt = userRepository.findByEmail(principal.getName());
|
||||
if (userOpt.isEmpty()) return ResponseEntity.status(401).build();
|
||||
UUID userId = userOpt.get().getUserId();
|
||||
|
||||
Map<String, NotificationPreferenceEntity> byKey = notificationPreferenceRepository.findByUserId(userId)
|
||||
.stream().collect(Collectors.toMap(p -> p.getCause().name(), p -> p));
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
for (MessageCause cause : MessageCause.values()) {
|
||||
NotificationPreferenceEntity pref = byKey.getOrDefault(
|
||||
cause.name(), NotificationPreferenceEntity.defaultFor(userId, cause));
|
||||
Map<String, Object> 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<Void> updateNotifications(@RequestBody Map<String, NotificationPreferenceRequest> 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<String> spieltMit, List<String> rollen, List<String> werkzeuge) {}
|
||||
|
||||
@GetMapping("/me/bdsm-defaults")
|
||||
public ResponseEntity<Map<String, Object>> 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<String, Object> 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<Void> 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<String> splitOrEmpty(String s) {
|
||||
if (s == null || s.isBlank()) return List.of();
|
||||
return List.of(s.split(","));
|
||||
}
|
||||
|
||||
@PutMapping("/me/geburtsdatum")
|
||||
public ResponseEntity<Void> 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<Void> 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Aufgaben – XXX The Game</title>
|
||||
@@ -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() {
|
||||
|
||||
128
xxxthegame/src/main/resources/static/bdsm-einladung.html
Normal file
128
xxxthegame/src/main/resources/static/bdsm-einladung.html
Normal file
@@ -0,0 +1,128 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Einladung – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.invite-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 14px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.invite-icon { font-size: 2.5rem; margin-bottom: 1rem; }
|
||||
.invite-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 0.5rem; }
|
||||
.invite-sub { font-size: 0.9rem; color: var(--color-muted); margin-bottom: 2rem; line-height: 1.6; }
|
||||
.invite-actions { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.invite-actions button { width: 100%; padding: 0.85rem; }
|
||||
.decline-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.82rem;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
padding: 0.25rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="app">
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<div id="loading" style="text-align:center;color:var(--color-muted);padding:3rem 0;">Einladung wird geladen…</div>
|
||||
<div class="invite-card" id="card" style="display:none;">
|
||||
<div class="invite-icon">⛓️</div>
|
||||
<div class="invite-title" id="title"></div>
|
||||
<div class="invite-sub" id="sub"></div>
|
||||
<div class="message" id="message" style="display:none;margin-bottom:1rem;"></div>
|
||||
<div class="invite-actions" id="actions"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const einladungId = params.get('id');
|
||||
if (!einladungId) window.location.replace('/userhome.html');
|
||||
|
||||
let einladung = null;
|
||||
|
||||
async function laden() {
|
||||
try {
|
||||
const res = await fetch(`/bdsm/einladung/${einladungId}`);
|
||||
if (!res.ok) { zeigeFehler('Einladung nicht gefunden.'); return; }
|
||||
einladung = await res.json();
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('card').style.display = '';
|
||||
|
||||
if (einladung.status === 'ACCEPTED_OWN' || einladung.status === 'ACCEPTED_HOST') {
|
||||
zeigeBestaetigt();
|
||||
return;
|
||||
}
|
||||
if (einladung.status === 'DECLINED' || einladung.status === 'CANCELLED') {
|
||||
zeigeFehler('Diese Einladung ist nicht mehr gültig.');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('title').textContent = `${einladung.inviterName || 'Jemand'} lädt dich ein`;
|
||||
document.getElementById('sub').textContent = 'Du wurdest zu einem BDSM Game eingeladen. Wie möchtest du mitspielen?';
|
||||
const actions = document.getElementById('actions');
|
||||
actions.innerHTML = `
|
||||
<button onclick="antworten(true, 'OWN_DEVICE')">Am eigenen Gerät mitspielen</button>
|
||||
<button class="secondary" onclick="antworten(true, 'HOST_DEVICE')">Am Gerät von ${einladung.inviterName || 'der einladenden Person'}</button>
|
||||
<button class="decline-btn" onclick="antworten(false, null)">Einladung ablehnen</button>`;
|
||||
} catch (e) {
|
||||
zeigeFehler('Fehler beim Laden der Einladung.');
|
||||
}
|
||||
}
|
||||
|
||||
async function antworten(accepted, mode) {
|
||||
document.getElementById('actions').innerHTML = '<div style="color:var(--color-muted);font-size:0.9rem;">Wird gespeichert…</div>';
|
||||
try {
|
||||
const res = await fetch(`/bdsm/einladung/${einladungId}/antwort`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ accepted, mode }),
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
if (!accepted) {
|
||||
document.getElementById('title').textContent = 'Einladung abgelehnt';
|
||||
document.getElementById('sub').textContent = 'Du hast die Einladung abgelehnt.';
|
||||
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||
} else if (mode === 'OWN_DEVICE') {
|
||||
window.location.replace(`/bdsmwarten.html?id=${einladungId}`);
|
||||
} else {
|
||||
zeigeBestaetigt();
|
||||
}
|
||||
} catch (_) {
|
||||
document.getElementById('actions').innerHTML = '';
|
||||
zeigeFehler('Fehler beim Speichern der Antwort.');
|
||||
}
|
||||
}
|
||||
|
||||
function zeigeBestaetigt() {
|
||||
document.getElementById('title').textContent = 'Einladung angenommen';
|
||||
document.getElementById('sub').textContent = 'Du spielst am Gerät der einladenden Person mit. Das Spiel wird dort von ihr gestartet.';
|
||||
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||
}
|
||||
|
||||
function zeigeFehler(text) {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('card').style.display = '';
|
||||
document.getElementById('title').textContent = 'Hinweis';
|
||||
document.getElementById('sub').textContent = text;
|
||||
document.getElementById('actions').innerHTML = '<button onclick="window.location.href=\'/userhome.html\'">Zur Startseite</button>';
|
||||
}
|
||||
|
||||
laden();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Neue Session – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.session-setup { max-width: 540px; }
|
||||
.session-setup { }
|
||||
|
||||
.setup-section { margin-bottom: 2.5rem; }
|
||||
.setup-section h2 {
|
||||
@@ -180,7 +180,7 @@
|
||||
aufgabenProLevel: parseInt(document.getElementById('sldAufgaben').value),
|
||||
zeitfaktorZeitstrafen: parseInt(document.getElementById('sldZeit').value) / 10,
|
||||
}));
|
||||
window.location.href = '/sessionbdsmplayers.html';
|
||||
window.location.href = '/bdsmplayers.html';
|
||||
}
|
||||
|
||||
function showMessage(text, type) {
|
||||
@@ -224,7 +224,7 @@
|
||||
function sessionFortfahren(sid) {
|
||||
BDSM_STORAGE_KEYS.forEach(k => sessionStorage.removeItem(k));
|
||||
sessionStorage.setItem('bdsm-session-id', sid);
|
||||
window.location.href = '/sessionbdsmingame.html';
|
||||
window.location.href = '/bdsmingame.html';
|
||||
}
|
||||
|
||||
function sessionBeendenFragen(sid) {
|
||||
@@ -241,7 +241,7 @@
|
||||
async function sessionLoeschen(sid) {
|
||||
versteckeModal();
|
||||
try {
|
||||
await fetch('/session', {
|
||||
await fetch('/bdsm', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionId: sid }),
|
||||
@@ -256,7 +256,7 @@
|
||||
if (!meRes.ok) return;
|
||||
const user = await meRes.json();
|
||||
|
||||
const sessionRes = await fetch(`/session?userId=${user.userId}`);
|
||||
const sessionRes = await fetch(`/bdsm?userId=${user.userId}`);
|
||||
if (sessionRes.status === 204) return;
|
||||
if (!sessionRes.ok) return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Im Spiel – XXX The Game</title>
|
||||
@@ -236,7 +236,12 @@
|
||||
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup') || 'null');
|
||||
const toys = JSON.parse(sessionStorage.getItem('bdsm-session-toys') || '[]');
|
||||
const sessionId = sessionStorage.getItem('bdsm-session-id');
|
||||
if (!sessionId) window.location.replace('/sessionbdsm.html');
|
||||
if (!sessionId) window.location.replace('/bdsm.html');
|
||||
|
||||
// Multi-Device: bin ich Gast?
|
||||
const isGuest = sessionStorage.getItem('bdsm-is-guest') === 'true';
|
||||
const myMitspielerId = sessionStorage.getItem('bdsm-guest-mitspieler-id') || null;
|
||||
let guestPollInterval = null;
|
||||
|
||||
// ── Modal ──
|
||||
function zeigeModal(title, text, actions) {
|
||||
@@ -283,6 +288,7 @@
|
||||
|
||||
function clearTimer() {
|
||||
if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }
|
||||
stopHostPoll();
|
||||
}
|
||||
|
||||
function zeigeTaskFehler(text) {
|
||||
@@ -310,7 +316,7 @@
|
||||
card.innerHTML = 'Aufgabe wird geladen…';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/session/${sessionId}/aufgaben/next`);
|
||||
const res = await fetch(`/bdsm/${sessionId}/aufgaben/next`);
|
||||
if (res.status === 204) { zeigeFinaleDialog(); return; }
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
currentTask = await res.json();
|
||||
@@ -326,9 +332,189 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ── Aktive Aufgabe persistieren ──
|
||||
async function saveAktiveAufgabe(task, timerStartedAt) {
|
||||
try {
|
||||
await fetch(`/bdsm/${sessionId}/active-task`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ taskJson: JSON.stringify(task), timerStartedAt }),
|
||||
});
|
||||
} catch (_) { /* ignorieren */ }
|
||||
}
|
||||
|
||||
async function clearAktiveAufgabe() {
|
||||
try {
|
||||
await fetch(`/bdsm/${sessionId}/active-task`, { method: 'DELETE' });
|
||||
} catch (_) { /* ignorieren */ }
|
||||
}
|
||||
|
||||
async function checkAktiveAufgabe() {
|
||||
try {
|
||||
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||
if (res.status === 204 || !res.ok) { ladeAufgabe(); return; }
|
||||
const data = await res.json();
|
||||
currentTask = JSON.parse(data.taskJson);
|
||||
if (currentTask.level) {
|
||||
document.getElementById('levelImg').src = `/img/lvl${currentTask.level}.png`;
|
||||
document.getElementById('levelDisplay').style.display = '';
|
||||
}
|
||||
if (data.elapsedSeconds !== null && data.elapsedSeconds !== undefined) {
|
||||
const remaining = currentTask.timer - data.elapsedSeconds;
|
||||
if (remaining <= 0) {
|
||||
await clearAktiveAufgabe();
|
||||
aufgabeAbgeschlossen();
|
||||
} else {
|
||||
restoreTimer(Math.floor(remaining));
|
||||
}
|
||||
} else {
|
||||
zeigeAufgabe();
|
||||
}
|
||||
} catch (_) { ladeAufgabe(); }
|
||||
}
|
||||
|
||||
function restoreTimer(remaining) {
|
||||
const task = currentTask;
|
||||
const card = document.getElementById('taskCard');
|
||||
card.className = 'task-card';
|
||||
card.innerHTML = `
|
||||
${badgeHtml(task.nameAktiverMitspieler)}
|
||||
<div class="task-text" title="${escapeAttr(task.aufgabeText)}">${task.aufgabeText}</div>
|
||||
<div class="task-footer">
|
||||
<div class="task-timer-row" id="taskActions">
|
||||
<div class="timer-big" id="timerValue">${formatTime(remaining)}</div>
|
||||
<button class="btn-sm-cancel" onclick="timerAbbrechen()">✕ Abbrechen</button>
|
||||
</div>
|
||||
${SESSION_BEENDEN_BTN}
|
||||
</div>`;
|
||||
let rem = remaining;
|
||||
timerInterval = setInterval(() => {
|
||||
rem--;
|
||||
const el = document.getElementById('timerValue');
|
||||
if (rem <= 0) {
|
||||
clearTimer();
|
||||
if (el) { el.textContent = formatTime(0); el.classList.add('expired'); }
|
||||
aufgabeAbgeschlossen();
|
||||
} else {
|
||||
if (el) el.textContent = formatTime(rem);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// ── Gast-Polling: wartet auf aktive Aufgabe für mein Gerät ──
|
||||
function startGastPoll() {
|
||||
if (guestPollInterval) return;
|
||||
const card = document.getElementById('taskCard');
|
||||
card.className = 'task-card loading';
|
||||
card.innerHTML = 'Warte auf Aufgabe…';
|
||||
guestPollInterval = setInterval(pollGastAufgabe, 2500);
|
||||
}
|
||||
|
||||
async function pollGastAufgabe() {
|
||||
try {
|
||||
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||
if (res.status === 204) {
|
||||
// Keine aktive Aufgabe – warten
|
||||
const card = document.getElementById('taskCard');
|
||||
if (!card.classList.contains('loading')) {
|
||||
card.className = 'task-card loading';
|
||||
card.innerHTML = 'Warte auf Aufgabe…';
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
const task = JSON.parse(data.taskJson);
|
||||
|
||||
if (task.mitspielerId && task.mitspielerId === myMitspielerId) {
|
||||
// Meine Aufgabe!
|
||||
clearInterval(guestPollInterval); guestPollInterval = null;
|
||||
currentTask = task;
|
||||
if (task.level) {
|
||||
document.getElementById('levelImg').src = `/img/lvl${task.level}.png`;
|
||||
document.getElementById('levelDisplay').style.display = '';
|
||||
}
|
||||
if (data.elapsedSeconds !== null && data.elapsedSeconds !== undefined && task.timer != null) {
|
||||
const remaining = task.timer - data.elapsedSeconds;
|
||||
if (remaining <= 0) { await clearAktiveAufgabe(); gastAufgabeAbgeschlossen(); }
|
||||
else restoreTimer(Math.floor(remaining));
|
||||
} else {
|
||||
zeigeGastAufgabe(task);
|
||||
}
|
||||
} else {
|
||||
// Jemand anderes ist dran
|
||||
const card = document.getElementById('taskCard');
|
||||
const name = task.nameAktiverMitspieler || 'Jemand';
|
||||
card.className = 'task-card loading';
|
||||
card.innerHTML = `<div style="font-size:1rem;color:var(--color-muted);">${name} ist dran…</div>`;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function zeigeGastAufgabe(task) {
|
||||
// Gast sieht seine Aufgabe mit eigenem "Erledigt"-Button
|
||||
const cb = task.callback;
|
||||
if (task.timer != null && !data?.elapsedSeconds) zeigeTimerAufgabe(task);
|
||||
else if (cb && cb.sperreId != null) zeigeSperreAufgabe(task);
|
||||
else if (cb && cb.faktor != null) zeigeVerlaengernAufgabe(task);
|
||||
else zeigeEinfacheAufgabe(task);
|
||||
}
|
||||
|
||||
async function gastAufgabeAbgeschlossen() {
|
||||
clearTimer();
|
||||
await clearAktiveAufgabe();
|
||||
const card = document.getElementById('taskCard');
|
||||
card.className = 'task-card loading';
|
||||
card.innerHTML = 'Aufgabe erledigt – warte auf nächste Aufgabe…';
|
||||
startGastPoll();
|
||||
}
|
||||
|
||||
// ── Host: wenn Aufgabe einem eigenem-Gerät-Spieler gehört ──
|
||||
let hostPollInterval = null;
|
||||
|
||||
function startHostPoll() {
|
||||
if (hostPollInterval) return;
|
||||
hostPollInterval = setInterval(pollHostAktiv, 2500);
|
||||
}
|
||||
|
||||
function stopHostPoll() {
|
||||
if (hostPollInterval) { clearInterval(hostPollInterval); hostPollInterval = null; }
|
||||
}
|
||||
|
||||
async function pollHostAktiv() {
|
||||
try {
|
||||
const res = await fetch(`/bdsm/${sessionId}/active-task`);
|
||||
if (res.status === 204) {
|
||||
// Gast hat Aufgabe erledigt → Host lädt nächste
|
||||
stopHostPoll();
|
||||
ladeAufgabe();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function zeigeAufgabe() {
|
||||
const task = currentTask;
|
||||
const cb = task.callback;
|
||||
saveAktiveAufgabe(task, null);
|
||||
|
||||
// Wenn Aufgabe für eigenes-Gerät-Spieler: Host zeigt nur Hinweis
|
||||
if (!isGuest && task.eigenesGeraet && task.mitspielerId) {
|
||||
const name = task.nameAktiverMitspieler || 'Jemand';
|
||||
const card = document.getElementById('taskCard');
|
||||
card.className = 'task-card';
|
||||
card.innerHTML = `
|
||||
<div style="flex:1;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:0.5rem;">
|
||||
<div style="font-size:1.8rem;">📱</div>
|
||||
<div style="font-size:1rem;color:var(--color-muted);text-align:center;">${name} spielt gerade auf dem eigenen Gerät.</div>
|
||||
</div>
|
||||
<div class="task-footer">
|
||||
<div class="task-btns"></div>
|
||||
${SESSION_BEENDEN_BTN}
|
||||
</div>`;
|
||||
startHostPoll();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cb && cb.sperreId != null) zeigeSperreAufgabe(task);
|
||||
else if (cb && cb.faktor != null) zeigeVerlaengernAufgabe(task);
|
||||
else if (task.timer != null) zeigeTimerAufgabe(task);
|
||||
@@ -380,6 +566,7 @@
|
||||
actions.innerHTML = `
|
||||
<div class="timer-big" id="timerValue">${formatTime(remaining)}</div>
|
||||
<button class="btn-sm-cancel" onclick="timerAbbrechen()">✕ Abbrechen</button>`;
|
||||
saveAktiveAufgabe(task, new Date().toISOString());
|
||||
|
||||
timerInterval = setInterval(() => {
|
||||
remaining--;
|
||||
@@ -405,8 +592,10 @@
|
||||
|
||||
async function aufgabeAbgeschlossen() {
|
||||
clearTimer();
|
||||
await clearAktiveAufgabe();
|
||||
if (isGuest) { gastAufgabeAbgeschlossen(); return; }
|
||||
try {
|
||||
const res = await fetch(`/session/sperre/abgelaufene?sessionId=${sessionId}`);
|
||||
const res = await fetch(`/bdsm/sperre/abgelaufene?sessionId=${sessionId}`);
|
||||
if (res.ok) {
|
||||
const text = await res.text();
|
||||
const texte = text.split(';').map(t => t.trim()).filter(t => t.length > 0);
|
||||
@@ -429,7 +618,7 @@
|
||||
const cb = currentTask?.callback;
|
||||
if (!cb) return;
|
||||
try {
|
||||
const res = await fetch('/session/sperre', {
|
||||
const res = await fetch('/bdsm/sperre', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ...cb, sessionId }),
|
||||
@@ -445,7 +634,7 @@
|
||||
const cb = currentTask?.callback;
|
||||
if (!cb) return;
|
||||
try {
|
||||
const res = await fetch('/session/sperre/verlaengern', {
|
||||
const res = await fetch('/bdsm/sperre/verlaengern', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ...cb, sessionId }),
|
||||
@@ -478,14 +667,14 @@
|
||||
|
||||
async function zurueckZuLevel5() {
|
||||
try {
|
||||
await fetch(`/session/${sessionId}/backToLevel5`, { method: 'POST' });
|
||||
await fetch(`/bdsm/${sessionId}/backToLevel5`, { method: 'POST' });
|
||||
} catch (_) {}
|
||||
ladeAufgabe();
|
||||
}
|
||||
|
||||
async function starteFinale() {
|
||||
try {
|
||||
const res = await fetch(`/session/sperre/aktive?sessionId=${sessionId}`);
|
||||
const res = await fetch(`/bdsm/sperre/aktive?sessionId=${sessionId}`);
|
||||
if (res.ok) {
|
||||
const sperren = await res.json();
|
||||
const texte = (sperren || []).map(s => s.releaseText).filter(t => t);
|
||||
@@ -506,7 +695,7 @@
|
||||
|
||||
async function ladeFinisher() {
|
||||
try {
|
||||
const res = await fetch(`/session/${sessionId}/finisher`);
|
||||
const res = await fetch(`/bdsm/${sessionId}/finisher`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const liste = await res.json();
|
||||
naechsterFinisher(liste, 0);
|
||||
@@ -572,7 +761,7 @@
|
||||
async function sessionLoeschen() {
|
||||
versteckeModal();
|
||||
try {
|
||||
await fetch('/session', {
|
||||
await fetch('/bdsm', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ sessionId }),
|
||||
@@ -583,7 +772,11 @@
|
||||
}
|
||||
|
||||
// ── Start ──
|
||||
ladeAufgabe();
|
||||
if (isGuest) {
|
||||
startGastPoll();
|
||||
} else {
|
||||
checkAktiveAufgabe();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
743
xxxthegame/src/main/resources/static/bdsmplayers.html
Normal file
743
xxxthegame/src/main/resources/static/bdsmplayers.html
Normal file
@@ -0,0 +1,743 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Mitspieler – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.session-setup { }
|
||||
|
||||
.setup-section { margin-bottom: 2.5rem; }
|
||||
.setup-section h2 {
|
||||
color: var(--color-primary);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.25rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
/* ── Player cards ── */
|
||||
.player-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 10px;
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.player-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.player-title {
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
}
|
||||
.player-badge {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
font-size: 0.7rem;
|
||||
padding: 0.1rem 0.5rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.player-badge-pending {
|
||||
background: var(--color-secondary);
|
||||
color: var(--color-muted);
|
||||
font-size: 0.7rem;
|
||||
padding: 0.1rem 0.5rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.player-badge-accepted {
|
||||
background: #1a5c2a;
|
||||
color: #6fcf97;
|
||||
font-size: 0.7rem;
|
||||
padding: 0.1rem 0.5rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.player-remove {
|
||||
margin-left: auto;
|
||||
background: transparent;
|
||||
border: 1px solid var(--color-secondary);
|
||||
color: var(--color-muted);
|
||||
padding: 0.25rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: normal;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
}
|
||||
.player-remove:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
background: transparent;
|
||||
}
|
||||
.btn-invite {
|
||||
background: transparent;
|
||||
border: 1px solid var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
padding: 0.45rem 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.btn-invite:hover { background: var(--color-primary); color: #fff; }
|
||||
.btn-cancel-invite {
|
||||
background: transparent;
|
||||
border: 1px solid var(--color-secondary);
|
||||
color: var(--color-muted);
|
||||
padding: 0.2rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: normal;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-cancel-invite:hover { border-color: var(--color-primary); color: var(--color-primary); background: transparent; }
|
||||
|
||||
.pending-info {
|
||||
text-align: center;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.9rem;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
.pending-name {
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.pending-mode {
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.card-field { margin-bottom: 1rem; }
|
||||
.card-field > label {
|
||||
font-size: 0.8rem;
|
||||
color: #aaa;
|
||||
margin: 0 0 0.5rem 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.check-group { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
||||
.check-group--two-col { display: grid; grid-template-columns: 1fr 1fr; }
|
||||
.check-item {
|
||||
display: inline-flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.45rem;
|
||||
background: var(--color-secondary);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px;
|
||||
padding: 0.4rem 0.7rem;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
user-select: none;
|
||||
}
|
||||
.check-item.is-checked { border-color: var(--color-primary); }
|
||||
.check-item input { accent-color: var(--color-primary); width: auto; margin-top: 0.15rem; cursor: pointer; flex-shrink: 0; }
|
||||
.check-item-label { font-size: 0.88rem; color: var(--color-text); line-height: 1.3; }
|
||||
.check-item-desc { display: block; font-size: 0.72rem; color: var(--color-muted); margin-top: 0.1rem; }
|
||||
|
||||
.add-player-btn {
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
border: 1px dashed var(--color-secondary);
|
||||
color: var(--color-muted);
|
||||
padding: 0.75rem;
|
||||
border-radius: 10px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, color 0.15s;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.add-player-btn:hover { border-color: var(--color-primary); color: var(--color-text); background: transparent; }
|
||||
|
||||
.field-error { font-size: 0.78rem; color: var(--color-primary); margin-top: 0.3rem; display: none; }
|
||||
|
||||
/* ── Freunde-Modal ── */
|
||||
.modal-overlay {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(0,0,0,0.75);
|
||||
z-index: 1000;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.modal-card {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 14px;
|
||||
padding: 1.75rem;
|
||||
max-width: 420px; width: 100%;
|
||||
}
|
||||
.modal-title { font-size: 1rem; font-weight: 700; margin-bottom: 1rem; }
|
||||
.check-item.is-disabled { opacity: 0.5; pointer-events: none; cursor: default; }
|
||||
.friend-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; background: var(--color-secondary); flex-shrink: 0; }
|
||||
.friend-combobox { position: relative; }
|
||||
.friend-dropdown {
|
||||
display: none; position: absolute; top: 100%; left: 0; right: 0;
|
||||
background: var(--color-card); border: 1px solid var(--color-secondary);
|
||||
border-radius: 8px; max-height: 220px; overflow-y: auto; z-index: 10; margin-top: 0.25rem;
|
||||
}
|
||||
.friend-dropdown-item {
|
||||
display: flex; align-items: center; gap: 0.75rem;
|
||||
padding: 0.6rem 0.75rem; cursor: pointer; transition: background 0.1s;
|
||||
font-size: 0.9rem; font-weight: 600;
|
||||
}
|
||||
.friend-dropdown-item:hover { background: var(--color-secondary); }
|
||||
.selected-friend-box {
|
||||
display: none; margin-top: 0.75rem; padding: 0.6rem 0.75rem;
|
||||
background: var(--color-secondary); border-radius: 8px;
|
||||
font-size: 0.9rem; font-weight: 600;
|
||||
border: 1px solid var(--color-primary); color: var(--color-text);
|
||||
}
|
||||
.modal-cancel { margin-top: 0.6rem; width: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="app">
|
||||
|
||||
<div class="modal-overlay" id="friendModal" style="display:none;">
|
||||
<div class="modal-card">
|
||||
<div class="modal-title">Freund einladen</div>
|
||||
<div class="friend-combobox">
|
||||
<input type="text" id="friendSearch" placeholder="Name eingeben…" autocomplete="off" oninput="filterFreunde(this.value)">
|
||||
<div class="friend-dropdown" id="friendDropdown"></div>
|
||||
</div>
|
||||
<div class="selected-friend-box" id="selectedFriendBox"></div>
|
||||
<button id="btnEinladen" style="margin-top:1rem; width:100%;" disabled onclick="confirmedEinladen()">Einladen</button>
|
||||
<button class="secondary modal-cancel" onclick="schliesseFriendModal()">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="content session-setup">
|
||||
|
||||
<h1>BDSM Game</h1>
|
||||
<p style="margin-bottom:2rem;">Schritt 2 von 4 – Mitspieler</p>
|
||||
|
||||
<div class="setup-section">
|
||||
<h2>Mitspieler</h2>
|
||||
<div id="playersContainer"></div>
|
||||
<button class="add-player-btn" onclick="addPlayer()">+ Spieler hinzufügen</button>
|
||||
</div>
|
||||
|
||||
<div class="message" id="message"></div>
|
||||
<div style="display:flex; gap:1rem;">
|
||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsm.html'">← Zurück</button>
|
||||
<button style="flex:2;" id="weiterBtn" onclick="weiter()">Weiter</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
if (!sessionStorage.getItem('bdsm-session-settings')) {
|
||||
window.location.replace('/bdsm.html');
|
||||
}
|
||||
|
||||
// SetupId erzeugen (persistent über sessionStorage)
|
||||
if (!sessionStorage.getItem('bdsm-setup-id')) {
|
||||
sessionStorage.setItem('bdsm-setup-id', crypto.randomUUID());
|
||||
}
|
||||
const setupId = sessionStorage.getItem('bdsm-setup-id');
|
||||
|
||||
const GESCHLECHTER = [
|
||||
{ value: 'MAENNLICH', label: 'Männlich' },
|
||||
{ value: 'WEIBLICH', label: 'Weiblich' },
|
||||
{ value: 'DIVERS', label: 'Divers' },
|
||||
];
|
||||
const ROLLEN = [
|
||||
{ value: 'AUFGABE_AKTIV', label: 'Aufgabe – Aktiv' },
|
||||
{ value: 'AUFGABE_PASSIV', label: 'Aufgabe – Passiv' },
|
||||
{ value: 'BESTRAFUNG_AKTIV', label: 'Bestrafung – Aktiv' },
|
||||
{ value: 'BESTRAFUNG_PASSIV', label: 'Bestrafung – Passiv' },
|
||||
];
|
||||
const WERKZEUGE_DEFAULTS = {
|
||||
MAENNLICH: ['MUND', 'PENIS', 'ANUS', 'UMSCHNALLDILDO'],
|
||||
WEIBLICH: ['MUND', 'VAGINA', 'ANUS', 'UMSCHNALLDILDO'],
|
||||
DIVERS: ['MUND', 'ANUS', 'UMSCHNALLDILDO'],
|
||||
};
|
||||
const WERKZEUGE = [
|
||||
{ value: 'MUND', label: 'Mund', desc: 'Gewillt den Mund einzusetzen' },
|
||||
{ value: 'VAGINA', label: 'Vagina', desc: 'Verfügt über eine Vagina und setzt sie ein' },
|
||||
{ value: 'PENIS', label: 'Penis', desc: 'Verfügt über einen Penis und setzt ihn ein' },
|
||||
{ value: 'ANUS', label: 'Anus', desc: 'Gewillt den Anus einzusetzen' },
|
||||
{ value: 'UMSCHNALLDILDO', label: 'Umschnall-Dildo', desc: 'Verfügt über einen Umschnall-Dildo' },
|
||||
];
|
||||
const ROLE_LABELS = {
|
||||
AUFGABE_AKTIV: 'Aufgabe – Aktiv', AUFGABE_PASSIV: 'Aufgabe – Passiv',
|
||||
BESTRAFUNG_AKTIV: 'Bestrafung – Aktiv', BESTRAFUNG_PASSIV: 'Bestrafung – Passiv',
|
||||
};
|
||||
|
||||
let playerSeq = 0;
|
||||
let playerIds = [];
|
||||
// { [playerId]: { einladungId, status, inviteeId, inviteeName, mode } | null }
|
||||
let playerInvitations = {};
|
||||
let pollIntervalId = null;
|
||||
let myUserId = null;
|
||||
let freundeListe = [];
|
||||
|
||||
function buildCheckItems(name, items, type, disabled = false) {
|
||||
return items.map(({ value, label, desc }) => `
|
||||
<label class="check-item${disabled ? ' is-disabled' : ''}">
|
||||
<input type="${type}" name="${name}" value="${value}"${disabled ? ' disabled' : ''}>
|
||||
<span>
|
||||
<span class="check-item-label">${label}</span>
|
||||
${desc ? `<span class="check-item-desc">${desc}</span>` : ''}
|
||||
</span>
|
||||
</label>`).join('');
|
||||
}
|
||||
|
||||
function createCardHtml(id, prefillName, isSelf) {
|
||||
const badge = isSelf ? '<span class="player-badge">Du</span>' : '';
|
||||
const num = playerIds.indexOf(id) + 1;
|
||||
const nameField = isSelf
|
||||
? `<input type="text" id="p${id}-name" value="${prefillName}" readonly style="background:transparent;cursor:default;color:var(--color-muted);">`
|
||||
: `<input type="text" id="p${id}-name" value="${prefillName}" placeholder="Name" autocomplete="off">`;
|
||||
const inviteBtn = isSelf ? '' : `<button class="btn-invite" onclick="oeffneFreundeModal(${id})">👥 Einladen</button>`;
|
||||
return `
|
||||
<div class="player-card" id="player-${id}">
|
||||
<div class="player-card-header">
|
||||
<span class="player-title">Spieler ${num}</span>
|
||||
${badge}
|
||||
${inviteBtn}
|
||||
<button class="player-remove" onclick="removePlayer(${id})">✕ Entfernen</button>
|
||||
</div>
|
||||
<div id="p${id}-body">
|
||||
${buildPlayerBody(id, nameField, isSelf)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function buildPlayerBody(id, nameField, genderDisabled = false) {
|
||||
return `
|
||||
<div class="card-field">
|
||||
<label>Name</label>
|
||||
${nameField}
|
||||
<div class="field-error" id="p${id}-name-err">Bitte Namen eingeben.</div>
|
||||
</div>
|
||||
<div class="card-field">
|
||||
<label>Geschlecht${genderDisabled ? ' <span style="font-size:0.75rem;color:var(--color-muted);">(unveränderlich)</span>' : ''}</label>
|
||||
<div class="check-group">${buildCheckItems('p' + id + '-geschlecht', GESCHLECHTER, 'radio', genderDisabled)}</div>
|
||||
<div class="field-error" id="p${id}-geschlecht-err">Bitte Geschlecht auswählen.</div>
|
||||
</div>
|
||||
<div class="card-field">
|
||||
<label>Spielt mit</label>
|
||||
<div class="check-group">${buildCheckItems('p' + id + '-spieltmit', GESCHLECHTER, 'checkbox')}</div>
|
||||
<div class="field-error" id="p${id}-spieltmit-err">Bitte mindestens eine Option wählen.</div>
|
||||
<div class="field-error" id="p${id}-partner-err">Kein Mitspieler mit passendem Geschlecht vorhanden.</div>
|
||||
</div>
|
||||
<div class="card-field">
|
||||
<label>Rollen</label>
|
||||
<div class="check-group">${buildCheckItems('p' + id + '-rollen', ROLLEN, 'checkbox')}</div>
|
||||
<div class="field-error" id="p${id}-rollen-err">Bitte mindestens eine Rolle wählen.</div>
|
||||
</div>
|
||||
<div class="card-field">
|
||||
<label>Verfügbar</label>
|
||||
<div class="check-group check-group--two-col">${buildCheckItems('p' + id + '-werkzeuge', WERKZEUGE, 'checkbox')}</div>
|
||||
<div class="field-error" id="p${id}-werkzeuge-err">Bitte mindestens ein Werkzeug wählen.</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function addPlayer(prefillName = '', isSelf = false) {
|
||||
playerSeq++;
|
||||
const id = playerSeq;
|
||||
playerIds.push(id);
|
||||
playerInvitations[id] = null;
|
||||
document.getElementById('playersContainer')
|
||||
.insertAdjacentHTML('beforeend', createCardHtml(id, prefillName, isSelf));
|
||||
refreshRemoveButtons();
|
||||
return id;
|
||||
}
|
||||
|
||||
function removePlayer(id) {
|
||||
const inv = playerInvitations[id];
|
||||
if (inv && (inv.status === 'PENDING' || inv.status === 'ACCEPTED_OWN' || inv.status === 'ACCEPTED_HOST')) {
|
||||
cancelEinladung(id);
|
||||
}
|
||||
document.getElementById('player-' + id)?.remove();
|
||||
playerIds = playerIds.filter(x => x !== id);
|
||||
delete playerInvitations[id];
|
||||
refreshPlayerTitles();
|
||||
refreshRemoveButtons();
|
||||
}
|
||||
|
||||
function refreshPlayerTitles() {
|
||||
playerIds.forEach((id, idx) => {
|
||||
const el = document.querySelector(`#player-${id} .player-title`);
|
||||
if (el) el.textContent = 'Spieler ' + (idx + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshRemoveButtons() {
|
||||
const canRemove = playerIds.length > 2;
|
||||
playerIds.forEach(id => {
|
||||
const btn = document.querySelector(`#player-${id} .player-remove`);
|
||||
if (btn) btn.style.display = canRemove ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('change', e => {
|
||||
const input = e.target;
|
||||
if (input.type !== 'checkbox' && input.type !== 'radio') return;
|
||||
if (input.type === 'radio') {
|
||||
document.querySelectorAll(`input[name="${input.name}"]`).forEach(r => {
|
||||
r.closest('.check-item')?.classList.toggle('is-checked', r.checked);
|
||||
});
|
||||
if (input.checked && input.name.endsWith('-geschlecht')) {
|
||||
const prefix = input.name.slice(0, -'-geschlecht'.length);
|
||||
const defaults = WERKZEUGE_DEFAULTS[input.value] || [];
|
||||
document.querySelectorAll(`input[name="${prefix}-werkzeuge"]`).forEach(cb => {
|
||||
cb.checked = defaults.includes(cb.value);
|
||||
cb.closest('.check-item')?.classList.toggle('is-checked', cb.checked);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
input.closest('.check-item')?.classList.toggle('is-checked', input.checked);
|
||||
}
|
||||
});
|
||||
|
||||
function getChecked(name) {
|
||||
return [...document.querySelectorAll(`input[name="${name}"]:checked`)].map(el => el.value);
|
||||
}
|
||||
|
||||
function setFieldError(id, show) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.style.display = show ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// ── Freunde-Modal ──
|
||||
let currentInvitePlayerId = null;
|
||||
let selectedFriend = null; // { userId, name }
|
||||
|
||||
async function oeffneFreundeModal(playerId) {
|
||||
currentInvitePlayerId = playerId;
|
||||
selectedFriend = null;
|
||||
document.getElementById('friendSearch').value = '';
|
||||
document.getElementById('friendDropdown').style.display = 'none';
|
||||
document.getElementById('friendDropdown').innerHTML = '';
|
||||
document.getElementById('selectedFriendBox').style.display = 'none';
|
||||
document.getElementById('selectedFriendBox').textContent = '';
|
||||
document.getElementById('btnEinladen').disabled = true;
|
||||
document.getElementById('friendModal').style.display = 'flex';
|
||||
if (freundeListe.length === 0) {
|
||||
try {
|
||||
const res = await fetch('/social/friends');
|
||||
freundeListe = res.ok ? await res.json() : [];
|
||||
} catch (_) { freundeListe = []; }
|
||||
}
|
||||
}
|
||||
|
||||
function filterFreunde(query) {
|
||||
selectedFriend = null;
|
||||
document.getElementById('selectedFriendBox').style.display = 'none';
|
||||
document.getElementById('btnEinladen').disabled = true;
|
||||
const dropdown = document.getElementById('friendDropdown');
|
||||
dropdown.innerHTML = '';
|
||||
const q = query.trim().toLowerCase();
|
||||
if (!q) { dropdown.style.display = 'none'; return; }
|
||||
const matches = freundeListe.filter(f => (f.user.name || '').toLowerCase().includes(q));
|
||||
if (!matches.length) {
|
||||
dropdown.innerHTML = '<div style="padding:0.6rem 0.75rem;color:var(--color-muted);font-size:0.9rem;">Keine Treffer.</div>';
|
||||
dropdown.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
matches.forEach(f => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'friend-dropdown-item';
|
||||
item.addEventListener('click', () => selectFriend(f.user.userId, f.user.name || 'Unbekannt'));
|
||||
if (f.user.profilePicture) {
|
||||
const img = document.createElement('img');
|
||||
img.className = 'friend-avatar';
|
||||
img.src = 'data:image/png;base64,' + f.user.profilePicture;
|
||||
img.alt = '';
|
||||
item.appendChild(img);
|
||||
} else {
|
||||
const av = document.createElement('div');
|
||||
av.className = 'friend-avatar';
|
||||
item.appendChild(av);
|
||||
}
|
||||
const span = document.createElement('span');
|
||||
span.textContent = f.user.name || 'Unbekannt';
|
||||
item.appendChild(span);
|
||||
dropdown.appendChild(item);
|
||||
});
|
||||
dropdown.style.display = 'block';
|
||||
}
|
||||
|
||||
function selectFriend(userId, name) {
|
||||
selectedFriend = { userId, name };
|
||||
document.getElementById('friendSearch').value = name;
|
||||
document.getElementById('friendDropdown').style.display = 'none';
|
||||
const box = document.getElementById('selectedFriendBox');
|
||||
box.textContent = '✓ ' + name;
|
||||
box.style.display = 'block';
|
||||
document.getElementById('btnEinladen').disabled = false;
|
||||
}
|
||||
|
||||
async function confirmedEinladen() {
|
||||
if (!selectedFriend) return;
|
||||
await einladen(selectedFriend.userId, selectedFriend.name);
|
||||
}
|
||||
|
||||
function schliesseFriendModal() {
|
||||
document.getElementById('friendModal').style.display = 'none';
|
||||
currentInvitePlayerId = null;
|
||||
selectedFriend = null;
|
||||
}
|
||||
|
||||
async function einladen(inviteeId, inviteeName) {
|
||||
const id = currentInvitePlayerId;
|
||||
schliesseFriendModal();
|
||||
if (!id) return;
|
||||
try {
|
||||
const res = await fetch('/bdsm/einladung', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ setupId, slotIndex: id, inviteeId }),
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
const data = await res.json();
|
||||
playerInvitations[id] = { einladungId: data.einladungId, status: 'PENDING', inviteeId, inviteeName };
|
||||
renderPending(id);
|
||||
startPoll();
|
||||
} catch (_) {
|
||||
showMessage('Einladung konnte nicht gesendet werden.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function renderPending(id) {
|
||||
const inv = playerInvitations[id];
|
||||
if (!inv) return;
|
||||
const body = document.getElementById(`p${id}-body`);
|
||||
if (!body) return;
|
||||
const headerInvBtn = document.querySelector(`#player-${id} .btn-invite`);
|
||||
if (headerInvBtn) headerInvBtn.style.display = 'none';
|
||||
|
||||
if (inv.status === 'PENDING') {
|
||||
// Badge
|
||||
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||
header.insertAdjacentHTML('afterbegin', `<span class="player-badge-pending">Ausstehend</span>`);
|
||||
body.innerHTML = `
|
||||
<div class="pending-info">
|
||||
<div class="pending-name">${inv.inviteeName}</div>
|
||||
<div>Einladung wurde gesendet – warte auf Antwort…</div>
|
||||
<button class="btn-cancel-invite" style="margin-top:1rem;" onclick="cancelEinladung(${id})">Einladung abbrechen</button>
|
||||
</div>`;
|
||||
} else if (inv.status === 'ACCEPTED_OWN' || inv.status === 'ACCEPTED_HOST') {
|
||||
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||
const modeLabel = inv.status === 'ACCEPTED_OWN' ? 'Eigenes Gerät' : 'Host-Gerät';
|
||||
header.insertAdjacentHTML('afterbegin', `<span class="player-badge-accepted">✓ ${modeLabel}</span>`);
|
||||
// Name readonly, Geschlecht gesperrt (kommt vom Profil der eingeladenen Person)
|
||||
const nameField = `<input type="text" id="p${id}-name" value="${inv.inviteeName}" readonly style="background:transparent;cursor:default;color:var(--color-muted);">`;
|
||||
body.innerHTML = buildPlayerBody(id, nameField, true);
|
||||
// Defaults laden falls verfügbar
|
||||
if (inv.defaults) {
|
||||
restorePlayer(id, {
|
||||
geschlecht: inv.defaults.geschlecht,
|
||||
spieltMit: inv.defaults.spieltMit || [],
|
||||
rollen: inv.defaults.rollen || [],
|
||||
werkzeuge: inv.defaults.werkzeuge || [],
|
||||
});
|
||||
}
|
||||
} else if (inv.status === 'DECLINED' || inv.status === 'CANCELLED') {
|
||||
// Slot wieder freigeben
|
||||
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||
if (headerInvBtn) headerInvBtn.style.display = '';
|
||||
playerInvitations[id] = null;
|
||||
body.innerHTML = buildPlayerBody(id, `<input type="text" id="p${id}-name" placeholder="Name" autocomplete="off">`);
|
||||
}
|
||||
}
|
||||
|
||||
async function cancelEinladung(id) {
|
||||
const inv = playerInvitations[id];
|
||||
if (!inv) return;
|
||||
await fetch(`/bdsm/einladung/${inv.einladungId}`, { method: 'DELETE' }).catch(() => {});
|
||||
playerInvitations[id] = null;
|
||||
// UI zurücksetzen
|
||||
const header = document.querySelector(`#player-${id} .player-card-header`);
|
||||
if (header) {
|
||||
header.querySelectorAll('.player-badge-pending,.player-badge-accepted').forEach(el => el.remove());
|
||||
const invBtn = header.querySelector('.btn-invite');
|
||||
if (invBtn) invBtn.style.display = '';
|
||||
}
|
||||
const body = document.getElementById(`p${id}-body`);
|
||||
if (body) body.innerHTML = buildPlayerBody(id, `<input type="text" id="p${id}-name" placeholder="Name" autocomplete="off">`);
|
||||
}
|
||||
|
||||
// ── Polling ──
|
||||
function startPoll() {
|
||||
if (pollIntervalId) return;
|
||||
pollIntervalId = setInterval(pollEinladungen, 3000);
|
||||
}
|
||||
|
||||
function stopPoll() {
|
||||
if (pollIntervalId) { clearInterval(pollIntervalId); pollIntervalId = null; }
|
||||
}
|
||||
|
||||
async function pollEinladungen() {
|
||||
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||
if (!hasPending) { stopPoll(); return; }
|
||||
try {
|
||||
const res = await fetch(`/bdsm/einladung?setupId=${setupId}`);
|
||||
if (!res.ok) return;
|
||||
const liste = await res.json();
|
||||
for (const e of liste) {
|
||||
const id = playerIds.find(pid => {
|
||||
const inv = playerInvitations[pid];
|
||||
return inv && inv.einladungId === e.einladungId;
|
||||
});
|
||||
if (!id) continue;
|
||||
const inv = playerInvitations[id];
|
||||
if (!inv || inv.status === e.status) continue;
|
||||
inv.status = e.status;
|
||||
if (e.status === 'ACCEPTED_OWN' || e.status === 'ACCEPTED_HOST') {
|
||||
// Defaults der eingeladenen Person laden
|
||||
try {
|
||||
const dRes = await fetch(`/user/${inv.inviteeId}/bdsm-defaults`);
|
||||
if (dRes.ok) inv.defaults = await dRes.json();
|
||||
} catch (_) {}
|
||||
}
|
||||
renderPending(id);
|
||||
}
|
||||
} catch (_) {}
|
||||
updateWeiterBtn();
|
||||
}
|
||||
|
||||
function updateWeiterBtn() {
|
||||
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||
document.getElementById('weiterBtn').disabled = hasPending;
|
||||
}
|
||||
|
||||
// ── Validation & Weiter ──
|
||||
function weiter() {
|
||||
hideMessage();
|
||||
|
||||
const hasPending = Object.values(playerInvitations).some(inv => inv && inv.status === 'PENDING');
|
||||
if (hasPending) {
|
||||
showMessage('Bitte warte, bis alle Einladungen beantwortet wurden.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
let valid = true;
|
||||
playerIds.forEach(id => setFieldError(`p${id}-partner-err`, false));
|
||||
|
||||
const mitspieler = playerIds.map(id => {
|
||||
const inv = playerInvitations[id];
|
||||
const name = document.getElementById(`p${id}-name`)?.value.trim() || '';
|
||||
const geschlecht = getChecked(`p${id}-geschlecht`);
|
||||
const spieltMit = getChecked(`p${id}-spieltmit`);
|
||||
const rollen = getChecked(`p${id}-rollen`);
|
||||
const werkzeuge = getChecked(`p${id}-werkzeuge`);
|
||||
|
||||
setFieldError(`p${id}-name-err`, !name);
|
||||
setFieldError(`p${id}-geschlecht-err`, geschlecht.length === 0);
|
||||
setFieldError(`p${id}-spieltmit-err`, spieltMit.length === 0);
|
||||
setFieldError(`p${id}-rollen-err`, rollen.length === 0);
|
||||
setFieldError(`p${id}-werkzeuge-err`, werkzeuge.length === 0);
|
||||
|
||||
if (!name || geschlecht.length === 0 || spieltMit.length === 0 || rollen.length === 0 || werkzeuge.length === 0) {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
geschlecht: geschlecht[0] || null,
|
||||
spieltMit, rollen, werkzeuge,
|
||||
userId: inv ? inv.inviteeId : null,
|
||||
eigenesGeraet: inv ? inv.status === 'ACCEPTED_OWN' : false,
|
||||
};
|
||||
});
|
||||
|
||||
if (!valid) { showMessage('Bitte alle Felder für jeden Spieler ausfüllen.', 'error'); return; }
|
||||
|
||||
const allRoles = new Set(mitspieler.flatMap(p => p.rollen));
|
||||
const missingRoles = Object.keys(ROLE_LABELS).filter(r => !allRoles.has(r));
|
||||
if (missingRoles.length > 0) {
|
||||
showMessage('Folgende Rollen müssen mindestens einmal vergeben sein: ' +
|
||||
missingRoles.map(r => ROLE_LABELS[r]).join(', '), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
let partnerFehler = false;
|
||||
mitspieler.forEach((player, i) => {
|
||||
const andereGeschlechter = mitspieler.filter((_, j) => j !== i).map(p => p.geschlecht);
|
||||
const hatPartner = player.spieltMit.some(g => andereGeschlechter.includes(g));
|
||||
if (!hatPartner) { setFieldError(`p${playerIds[i]}-partner-err`, true); partnerFehler = true; }
|
||||
});
|
||||
if (partnerFehler) { showMessage('Mindestens ein Spieler hat keinen kompatiblen Mitspieler.', 'error'); return; }
|
||||
|
||||
const settings = JSON.parse(sessionStorage.getItem('bdsm-session-settings'));
|
||||
sessionStorage.setItem('bdsm-session-setup', JSON.stringify({ settings, mitspieler }));
|
||||
window.location.href = '/bdsmtasks.html';
|
||||
}
|
||||
|
||||
function showMessage(text, type) {
|
||||
const el = document.getElementById('message');
|
||||
el.textContent = text; el.className = `message ${type}`; el.style.display = 'block';
|
||||
}
|
||||
function hideMessage() { document.getElementById('message').style.display = 'none'; }
|
||||
|
||||
function restorePlayer(id, data) {
|
||||
if (data.geschlecht) {
|
||||
const radio = document.querySelector(`input[name="p${id}-geschlecht"][value="${data.geschlecht}"]`);
|
||||
if (radio) { radio.checked = true; radio.closest('.check-item')?.classList.add('is-checked'); }
|
||||
}
|
||||
(data.spieltMit || []).forEach(val => {
|
||||
const cb = document.querySelector(`input[name="p${id}-spieltmit"][value="${val}"]`);
|
||||
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||
});
|
||||
(data.rollen || []).forEach(val => {
|
||||
const cb = document.querySelector(`input[name="p${id}-rollen"][value="${val}"]`);
|
||||
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||
});
|
||||
(data.werkzeuge || []).forEach(val => {
|
||||
const cb = document.querySelector(`input[name="p${id}-werkzeuge"][value="${val}"]`);
|
||||
if (cb) { cb.checked = true; cb.closest('.check-item')?.classList.add('is-checked'); }
|
||||
});
|
||||
}
|
||||
|
||||
// ── Init ──
|
||||
const savedSetup = sessionStorage.getItem('bdsm-session-setup');
|
||||
if (savedSetup) {
|
||||
const { mitspieler } = JSON.parse(savedSetup);
|
||||
mitspieler.forEach((p, i) => {
|
||||
const id = addPlayer(p.name, i === 0);
|
||||
restorePlayer(id, p);
|
||||
});
|
||||
} else {
|
||||
Promise.all([
|
||||
fetch('/login/me').then(r => r.ok ? r.json() : null).catch(() => null),
|
||||
fetch('/user/me/bdsm-defaults').then(r => r.ok ? r.json() : {}).catch(() => ({}))
|
||||
]).then(([user, defaults]) => {
|
||||
myUserId = user?.userId || null;
|
||||
addPlayer(user ? user.name : '', true);
|
||||
addPlayer();
|
||||
const selfId = playerIds[0];
|
||||
restorePlayer(selfId, {
|
||||
geschlecht: user?.geschlecht || null,
|
||||
spieltMit: defaults.spieltMit || [],
|
||||
rollen: defaults.rollen || [],
|
||||
werkzeuge: defaults.werkzeuge || [],
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Aufgaben-Gruppen – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.session-setup { max-width: 700px; }
|
||||
.session-setup { }
|
||||
|
||||
.setup-section { margin-bottom: 2.5rem; }
|
||||
.setup-section h2 {
|
||||
@@ -119,7 +119,7 @@
|
||||
<div style="position:relative; margin-top:2rem;">
|
||||
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
||||
<div style="display:flex; gap:1rem;">
|
||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/sessionbdsmplayers.html'">← Zurück</button>
|
||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsmplayers.html'">← Zurück</button>
|
||||
<button style="flex:2;" onclick="weiter()">Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,7 +130,7 @@
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
if (!sessionStorage.getItem('bdsm-session-setup')) {
|
||||
window.location.replace('/sessionbdsm.html');
|
||||
window.location.replace('/bdsm.html');
|
||||
}
|
||||
|
||||
const savedGruppen = new Set(JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || '[]'));
|
||||
@@ -308,18 +308,19 @@
|
||||
}
|
||||
|
||||
sessionStorage.setItem('bdsm-session-gruppen', JSON.stringify(selected));
|
||||
window.location.href = '/sessionbdsmtoys.html';
|
||||
window.location.href = '/bdsmtoys.html';
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
fetch('/gruppe/list/user?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
||||
fetch('/abo/list?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
||||
fetch('/gruppe/list/system?page=0&size=500').then(r => r.ok ? r.json() : { content: [] }),
|
||||
fetch('/gruppe/list/user?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] user HTTP', r.status), { content: [] })),
|
||||
fetch('/abo/list?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] abo HTTP', r.status), { content: [] })),
|
||||
fetch('/gruppe/list/system?page=0&size=500').then(r => r.ok ? r.json() : (console.warn('[bdsmtasks] system HTTP', r.status), { content: [] })),
|
||||
]).then(([own, abo, system]) => {
|
||||
console.log('[bdsmtasks] own:', own, 'abo:', abo, 'system:', system);
|
||||
renderList('listOwn', own.content || []);
|
||||
renderList('listSubscribed', abo.content || []);
|
||||
renderList('listSystem', system.content || []);
|
||||
});
|
||||
}).catch(err => console.error('[bdsmtasks] Fehler beim Laden:', err));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Toys – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.session-setup { max-width: 700px; }
|
||||
.session-setup { }
|
||||
|
||||
.setup-section { margin-bottom: 2.5rem; }
|
||||
.setup-section h2 {
|
||||
@@ -74,7 +74,7 @@
|
||||
<div style="position:relative; margin-top:2rem;">
|
||||
<div class="message" id="message" style="position:absolute; bottom:calc(100% + 0.5rem); left:0; right:0; margin:0;"></div>
|
||||
<div style="display:flex; gap:1rem;">
|
||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/sessionbdsmtasks.html'">← Zurück</button>
|
||||
<button style="flex:1;" class="secondary" onclick="window.location.href='/bdsmtasks.html'">← Zurück</button>
|
||||
<button style="flex:2;" onclick="spielStarten()">Spiel starten</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -85,7 +85,7 @@
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
const savedGruppen = JSON.parse(sessionStorage.getItem('bdsm-session-gruppen') || 'null');
|
||||
if (!savedGruppen) window.location.replace('/sessionbdsm.html');
|
||||
if (!savedGruppen) window.location.replace('/bdsm.html');
|
||||
|
||||
// Previously saved toy selection (when navigating back from game page)
|
||||
const savedToysRaw = sessionStorage.getItem('bdsm-session-toys');
|
||||
@@ -241,7 +241,7 @@
|
||||
|
||||
try {
|
||||
// 1. Session anlegen
|
||||
const sessionRes = await fetch('/session', {
|
||||
const sessionRes = await fetch('/bdsm', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -249,6 +249,7 @@
|
||||
wahrscheinlichkeitSperre: settings.wahrscheinlichkeitSperre,
|
||||
aufgabenProLevel: settings.aufgabenProLevel,
|
||||
zeitfaktorZeitstrafen: settings.zeitfaktorZeitstrafen,
|
||||
setupId: sessionStorage.getItem('bdsm-setup-id'),
|
||||
}),
|
||||
});
|
||||
if (!sessionRes.ok) throw new Error('Session konnte nicht angelegt werden.');
|
||||
@@ -258,7 +259,7 @@
|
||||
// 2. Mitspieler hinzufügen
|
||||
const setup = JSON.parse(sessionStorage.getItem('bdsm-session-setup'));
|
||||
for (const p of setup.mitspieler) {
|
||||
const res = await fetch(`/session/${sessionId}/mitspieler`, {
|
||||
const res = await fetch(`/bdsm/${sessionId}/mitspieler`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -267,13 +268,15 @@
|
||||
spieltMit: p.spieltMit,
|
||||
rollen: p.rollen,
|
||||
verfuegbareWerkzeuge: p.werkzeuge,
|
||||
userId: p.userId || null,
|
||||
eigenesGeraet: p.eigenesGeraet || false,
|
||||
}),
|
||||
});
|
||||
if (!res.ok) throw new Error(`Mitspieler "${p.name}" konnte nicht hinzugefügt werden.`);
|
||||
}
|
||||
|
||||
// 3. Aufgaben setzen
|
||||
const aufgabenRes = await fetch(`/session/${sessionId}/aufgaben`, {
|
||||
const aufgabenRes = await fetch(`/bdsm/${sessionId}/aufgaben`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(gameContent),
|
||||
@@ -281,7 +284,7 @@
|
||||
if (!aufgabenRes.ok) throw new Error('Aufgaben konnten nicht gespeichert werden.');
|
||||
|
||||
sessionStorage.setItem('bdsm-session-id', sessionId);
|
||||
window.location.href = '/sessionbdsmingame.html';
|
||||
window.location.href = '/bdsmingame.html';
|
||||
} catch (e) {
|
||||
showMessage(e.message, 'error');
|
||||
btn.disabled = false;
|
||||
92
xxxthegame/src/main/resources/static/bdsmwarten.html
Normal file
92
xxxthegame/src/main/resources/static/bdsmwarten.html
Normal file
@@ -0,0 +1,92 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BDSM Game – Warten – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.wait-card {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
}
|
||||
.wait-icon { font-size: 3rem; margin-bottom: 1.5rem; animation: pulse 2s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.4; } }
|
||||
.wait-title { font-size: 1.3rem; font-weight: 700; margin-bottom: 0.75rem; }
|
||||
.wait-sub { font-size: 0.9rem; color: var(--color-muted); line-height: 1.6; margin-bottom: 2rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="app">
|
||||
<div class="main">
|
||||
<div class="content wait-card">
|
||||
<div class="wait-icon">⏳</div>
|
||||
<div class="wait-title">Warte auf Spielstart…</div>
|
||||
<div class="wait-sub" id="sub">Der Host startet das Spiel in Kürze. Diese Seite aktualisiert sich automatisch.</div>
|
||||
<div class="message" id="message" style="display:none;"></div>
|
||||
<button class="secondary" onclick="abbrechen()">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const einladungId = params.get('id');
|
||||
if (!einladungId) window.location.replace('/userhome.html');
|
||||
|
||||
let pollInterval = null;
|
||||
|
||||
async function pruefen() {
|
||||
try {
|
||||
const res = await fetch(`/bdsm/einladung/${einladungId}`);
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
|
||||
if (data.status === 'CANCELLED') {
|
||||
stopPoll();
|
||||
zeigeFehler('Die Einladung wurde abgebrochen.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.sessionId) {
|
||||
stopPoll();
|
||||
// Meinen Mitspieler laden
|
||||
try {
|
||||
const mRes = await fetch(`/bdsm/${data.sessionId}/mitspieler/me`);
|
||||
if (mRes.ok) {
|
||||
const mData = await mRes.json();
|
||||
sessionStorage.setItem('bdsm-guest-mitspieler-id', mData.mitspielerId);
|
||||
sessionStorage.setItem('bdsm-guest-name', mData.name);
|
||||
}
|
||||
} catch (_) {}
|
||||
sessionStorage.setItem('bdsm-session-id', data.sessionId);
|
||||
sessionStorage.setItem('bdsm-is-guest', 'true');
|
||||
window.location.replace('/bdsmingame.html');
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function stopPoll() {
|
||||
if (pollInterval) { clearInterval(pollInterval); pollInterval = null; }
|
||||
}
|
||||
|
||||
function zeigeFehler(text) {
|
||||
document.getElementById('sub').style.display = 'none';
|
||||
const el = document.getElementById('message');
|
||||
el.textContent = text;
|
||||
el.className = 'message error';
|
||||
el.style.display = '';
|
||||
}
|
||||
|
||||
async function abbrechen() {
|
||||
stopPoll();
|
||||
await fetch(`/bdsm/einladung/${einladungId}`, { method: 'DELETE' }).catch(() => {});
|
||||
window.location.href = '/userhome.html';
|
||||
}
|
||||
|
||||
// Sofort prüfen, dann alle 3 Sekunden
|
||||
pruefen();
|
||||
pollInterval = setInterval(pruefen, 3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -406,11 +406,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs: Feed | Pinnwand | Lock-Historie -->
|
||||
<!-- Tabs: Feed | Pinnwand | Spielhistorie -->
|
||||
<div class="profil-tabs" style="margin-top:1.25rem;">
|
||||
<button class="profil-tab-btn active" id="tabBtnPosts" onclick="switchProfilTab('posts', this)">Feed</button>
|
||||
<button class="profil-tab-btn" id="tabBtnPinnwand" onclick="switchProfilTab('pinnwand', this)">Pinnwand</button>
|
||||
<button class="profil-tab-btn" id="tabBtnLockHistory" onclick="switchProfilTab('lockhistory', this); loadLockHistory()">Lock-Historie</button>
|
||||
<button class="profil-tab-btn" id="tabBtnGameHistory" onclick="switchProfilTab('gamehistory', this); loadGameHistory()">Spielhistorie</button>
|
||||
</div>
|
||||
|
||||
<!-- Feed Tab (vorausgewählt) -->
|
||||
@@ -429,10 +429,10 @@
|
||||
<div id="pinnwandList"></div>
|
||||
</div>
|
||||
|
||||
<!-- Lock-Historie Tab -->
|
||||
<div class="profil-tab-panel" id="tab-lockhistory">
|
||||
<div id="lockHistoryList" style="margin-top:0.75rem;"></div>
|
||||
<p id="lockHistoryEmpty" style="color:var(--color-muted);font-size:0.9rem;display:none;">Keine abgeschlossenen Locks vorhanden.</p>
|
||||
<!-- Spielhistorie Tab -->
|
||||
<div class="profil-tab-panel" id="tab-gamehistory">
|
||||
<div id="gameHistoryList" style="margin-top:0.75rem;"></div>
|
||||
<p id="gameHistoryEmpty" style="color:var(--color-muted);font-size:0.9rem;display:none;">Keine abgeschlossenen Locks vorhanden.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -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 = `<span class="label">${label}</span><span class="value">${esc(value)}</span>`;
|
||||
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 = `<span>${label}</span><a href="/einstellungen.html" style="font-size:0.82rem;color:var(--color-primary);">← Einstellungen</a>`;
|
||||
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 @@
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ── 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: '<span style="position:relative;display:inline-block;line-height:1;"><img src="/img/card.png" style="width:2.7rem;height:2.7rem;object-fit:contain;display:block;"><span style="position:absolute;bottom:-2px;right:-4px;font-size:1.3rem;line-height:1;">🔒</span></span>',
|
||||
TIMELOCK: '<span style="position:relative;display:inline-block;line-height:1;"><span style="font-size:2.7rem;">⏰</span><span style="position:absolute;bottom:-2px;right:-4px;font-size:1.3rem;line-height:1;">🔒</span></span>',
|
||||
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 = '<p style="color:var(--color-muted);font-size:0.85rem;">Lädt…</p>';
|
||||
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 ? `<span style="color:var(--color-muted);font-size:0.78rem;">Lockee: ${esc(e.lockeeName)}</span>` : '')
|
||||
: (e.keyholderName ? `<span style="color:var(--color-muted);font-size:0.78rem;">Keyholder: ${esc(e.keyholderName)}</span>` : '<span style="color:var(--color-muted);font-size:0.78rem;">Self-Lock</span>');
|
||||
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
|
||||
: `<span style="font-size:2.7rem;line-height:1;">${gameIconRaw}</span>`;
|
||||
|
||||
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
|
||||
? `<img src="data:image/jpeg;base64,${e.partnerPic}" style="width:40px;height:40px;border-radius:50%;object-fit:cover;display:block;">`
|
||||
: `<div style="width:40px;height:40px;border-radius:50%;background:var(--color-secondary);display:flex;align-items:center;justify-content:center;font-size:1.1rem;">👤</div>`;
|
||||
return `<div style="display:flex;align-items:center;gap:0.75rem;padding:0.65rem 0;border-bottom:1px solid var(--color-secondary);">
|
||||
<div style="position:relative;width:40px;height:40px;flex-shrink:0;">
|
||||
${avatar}
|
||||
<span style="position:absolute;top:-3px;left:-3px;font-size:1.4rem;line-height:1;">${icon}</span>
|
||||
</div>
|
||||
|
||||
const participants = (e.participants || []).map(p => {
|
||||
const badge = ROLE_BADGE[p.role] || '';
|
||||
const img = p.picture
|
||||
? `<img src="data:image/png;base64,${p.picture}" style="width:40px;height:40px;border-radius:50%;object-fit:cover;display:block;">`
|
||||
: `<div style="width:40px;height:40px;border-radius:50%;background:var(--color-secondary);display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0;">👤</div>`;
|
||||
return `<a href="/benutzer.html?userId=${esc(p.userId)}" style="position:relative;flex-shrink:0;text-decoration:none;" title="${esc(p.name || '')} (${p.role})">
|
||||
${img}
|
||||
${badge ? `<span style="position:absolute;top:-4px;right:-4px;font-size:0.9rem;line-height:1;">${badge}</span>` : ''}
|
||||
</a>`;
|
||||
}).join('');
|
||||
|
||||
return `<div style="display:flex;align-items:flex-start;gap:0.85rem;padding:0.75rem 0;border-bottom:1px solid var(--color-secondary);">
|
||||
<div style="flex-shrink:0;width:3rem;text-align:center;">${gameIcon}</div>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<div style="font-weight:600;font-size:0.92rem;">${esc(e.lockName) || 'Unbenanntes Lock'}</div>
|
||||
<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-top:0.15rem;">
|
||||
${partner}
|
||||
<span style="color:var(--color-muted);font-size:0.78rem;">⏱ ${dur}</span>
|
||||
<span style="color:var(--color-muted);font-size:0.78rem;">${fmtDate(e.unlockTime)}</span>
|
||||
</div>
|
||||
<div style="font-weight:600;font-size:0.92rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${esc(e.gameName) || 'Unbenannt'}</div>
|
||||
<div style="font-size:0.78rem;color:var(--color-muted);margin-top:0.15rem;">⏱ ${dur} · ${new Date(e.endTime).toLocaleDateString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric'})}</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:0.35rem;align-items:center;flex-shrink:0;">${participants}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
} catch(e) { list.innerHTML = ''; }
|
||||
@@ -954,7 +1041,7 @@
|
||||
: '◉';
|
||||
const bildRaw = bilderCarousel(p.bilder);
|
||||
const bildHtml = bildRaw
|
||||
? `<div class="post-bild-wrap" data-post-id="${p.postId}">${bildRaw}<div class="post-bild-hover-icon">💬</div></div>`
|
||||
? `<div class="post-bild-wrap" data-post-id="${p.postId}">${bildRaw}</div>`
|
||||
: '';
|
||||
const privacyLabel = p.isPublic ? '' : '<span style="font-size:0.72rem;color:var(--color-muted);margin-left:0.4rem;">🔒 privat</span>';
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bestätigungs-Modal -->
|
||||
<div class="confirm-modal-bg" id="confirmModal">
|
||||
<div class="confirm-modal-overlay" onclick="confirmCancel()"></div>
|
||||
<div class="confirm-modal-box">
|
||||
<button onclick="confirmCancel()" style="position:absolute;top:0.75rem;right:0.75rem;background:none;border:none;color:var(--color-muted);font-size:1.3rem;cursor:pointer;padding:0.2rem 0.5rem;line-height:1;" title="Schließen">✕</button>
|
||||
<div class="confirm-modal-title" id="confirmTitle"></div>
|
||||
<div class="confirm-modal-text" id="confirmText"></div>
|
||||
<div class="confirm-modal-actions">
|
||||
<button class="confirm-modal-cancel" onclick="confirmCancel()">Abbrechen</button>
|
||||
<button class="confirm-modal-ok" id="confirmOkBtn">Bestätigen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lockee-Einladungs-Dialog -->
|
||||
<div class="lockee-dialog-bg" id="lockeeInviteDialog">
|
||||
<div class="lockee-dialog-overlay" onclick="closeLockeeInviteDialog()"></div>
|
||||
@@ -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 {
|
||||
|
||||
968
xxxthegame/src/main/resources/static/einstellungen.html
Normal file
968
xxxthegame/src/main/resources/static/einstellungen.html
Normal file
@@ -0,0 +1,968 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/img/icon.png" type="image/png">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Einstellungen – XXX The Game</title>
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
.settings-page h1 {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* ── Accordion ── */
|
||||
.settings-section {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 10px;
|
||||
margin-bottom: 0.75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.settings-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.25rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.settings-section-header:hover {
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.settings-section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.settings-section-arrow {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-muted);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.settings-section.open .settings-section-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.settings-section-body {
|
||||
display: none;
|
||||
padding: 0 1.25rem 1.25rem;
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.settings-section.open .settings-section-body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ── Einstellungs-Zeilen ── */
|
||||
.settings-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.9rem 0;
|
||||
}
|
||||
|
||||
.settings-row-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-row-label {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.settings-row-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.settings-row select {
|
||||
width: auto;
|
||||
min-width: 150px;
|
||||
padding: 0.45rem 0.75rem;
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ── Benachrichtigungen Grid ── */
|
||||
.notif-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 5rem 5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.notif-grid-row {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.notif-grid-row > * {
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.notif-grid-row.notif-header > * {
|
||||
padding-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.notif-col-check {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.notif-col-check input[type="checkbox"] {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
accent-color: var(--color-primary, #c0392b);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── Speichern-Feedback (Toast) ── */
|
||||
.save-toast {
|
||||
position: fixed;
|
||||
bottom: 1.5rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(2rem);
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-left: 3px solid var(--color-success, #4caf50);
|
||||
border-radius: 8px;
|
||||
padding: 0.6rem 1.25rem;
|
||||
font-size: 0.88rem;
|
||||
color: var(--color-success, #4caf50);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
z-index: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.save-toast.visible {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
|
||||
/* ── Spiel-Einstellungen Check-Items ── */
|
||||
.spiel-check-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
.spiel-check-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
background: var(--color-secondary);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 6px;
|
||||
padding: 0.35rem 0.65rem;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
user-select: none;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.spiel-check-item.is-checked { border-color: var(--color-primary); }
|
||||
.spiel-check-item input {
|
||||
accent-color: var(--color-primary);
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
.spiel-subsection {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.spiel-subsection-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.4rem;
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
}
|
||||
.spiel-field { margin-bottom: 1rem; }
|
||||
.spiel-field > .settings-row-label { margin-bottom: 0.2rem; }
|
||||
|
||||
/* ── Modal ── */
|
||||
.modal-backdrop {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.6);
|
||||
z-index: 200;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-backdrop.visible { display: flex; }
|
||||
|
||||
.modal {
|
||||
background: var(--color-card);
|
||||
border: 1px solid var(--color-secondary);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
box-shadow: 0 8px 32px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.modal h2 {
|
||||
color: var(--color-primary);
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.modal-actions button { flex: 1; margin-top: 0; }
|
||||
|
||||
/* ── Separator ── */
|
||||
.settings-separator {
|
||||
border: none;
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
margin: 1.25rem 0 0;
|
||||
}
|
||||
|
||||
/* ── Vorschau-Buttons ── */
|
||||
.preview-row {
|
||||
padding-top: 1.25rem;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preview-row span {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-muted);
|
||||
flex-basis: 100%;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.preview-row button {
|
||||
width: auto;
|
||||
padding: 0.5rem 1.1rem;
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="app">
|
||||
<div class="main">
|
||||
<div class="content">
|
||||
<h1>⚙️ Einstellungen</h1>
|
||||
|
||||
<!-- Grunddaten -->
|
||||
<div class="settings-section" id="sec-grunddaten">
|
||||
<div class="settings-section-header" onclick="toggleSection('sec-grunddaten')">
|
||||
<span class="settings-section-title">👤 Grunddaten</span>
|
||||
<span class="settings-section-arrow">▸</span>
|
||||
</div>
|
||||
<div class="settings-section-body">
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Nickname</div>
|
||||
<div class="settings-row-desc" id="gd-name">…</div>
|
||||
</div>
|
||||
<button onclick="openNameDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">E-Mail</div>
|
||||
<div class="settings-row-desc" id="gd-email">…</div>
|
||||
</div>
|
||||
<button onclick="openEmailDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Geburtsdatum</div>
|
||||
<div class="settings-row-desc" id="gd-geburtsdatum">…</div>
|
||||
</div>
|
||||
<button onclick="openGeburtsdatumDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;">Ändern</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label" style="color:var(--color-primary);">Konto löschen</div>
|
||||
<div class="settings-row-desc">Alle Daten werden unwiderruflich gelöscht</div>
|
||||
</div>
|
||||
<button onclick="openDeleteDialog()" style="width:auto;padding:0.45rem 1rem;margin:0;font-size:0.9rem;background:transparent;border:1px solid var(--color-secondary);color:var(--color-muted);">Löschen</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Spiel Einstellungen -->
|
||||
<div class="settings-section" id="sec-spiel">
|
||||
<div class="settings-section-header" onclick="toggleSection('sec-spiel')">
|
||||
<span class="settings-section-title">🕹️ Spiel Einstellungen</span>
|
||||
<span class="settings-section-arrow">▸</span>
|
||||
</div>
|
||||
<div class="settings-section-body">
|
||||
|
||||
<!-- BDSM Game -->
|
||||
<div class="spiel-subsection">
|
||||
<div class="spiel-subsection-title">BDSM Game</div>
|
||||
|
||||
<div class="spiel-field">
|
||||
<div class="settings-row-label">Spiele mit Geschlecht</div>
|
||||
<div class="spiel-check-group" id="bdsm-spieltmit">
|
||||
<label class="spiel-check-item"><input type="checkbox" value="WEIBLICH"> Weiblich</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="DIVERS"> Divers</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="MAENNLICH"> Männlich</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spiel-field">
|
||||
<div class="settings-row-label">Meine Rollen</div>
|
||||
<div class="spiel-check-group" id="bdsm-rollen">
|
||||
<label class="spiel-check-item"><input type="checkbox" value="AUFGABE_AKTIV"> Aufgaben aktiv</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="AUFGABE_PASSIV"> Aufgaben passiv</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="BESTRAFUNG_AKTIV"> Bestrafungen aktiv</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="BESTRAFUNG_PASSIV"> Bestrafungen passiv</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spiel-field">
|
||||
<div class="settings-row-label">Was ich einsetze</div>
|
||||
<div class="spiel-check-group" id="bdsm-werkzeuge">
|
||||
<label class="spiel-check-item"><input type="checkbox" value="MUND"> Mund</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="VAGINA"> Vagina</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="PENIS"> Penis</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="ANUS"> Anus</label>
|
||||
<label class="spiel-check-item"><input type="checkbox" value="UMSCHNALLDILDO"> Umschnall-Dildo</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Benachrichtigungen -->
|
||||
<div class="settings-section" id="sec-benachrichtigungen">
|
||||
<div class="settings-section-header" onclick="toggleSection('sec-benachrichtigungen')">
|
||||
<span class="settings-section-title">🔔 Benachrichtigungen</span>
|
||||
<span class="settings-section-arrow">▸</span>
|
||||
</div>
|
||||
<div class="settings-section-body">
|
||||
<div class="notif-grid">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="notif-grid-row notif-header">
|
||||
<div></div>
|
||||
<div class="notif-col-check settings-row-label">In-App</div>
|
||||
<div class="notif-col-check settings-row-label">E-Mail</div>
|
||||
</div>
|
||||
|
||||
<!-- Einladungen -->
|
||||
<div class="notif-grid-row">
|
||||
<div>
|
||||
<div class="settings-row-label">Einladungen</div>
|
||||
<div class="settings-row-desc">Einladungen zu Locks und Spielen, Annahmen und Ablehnungen</div>
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-INVITATION-inApp" onchange="saveNotifications()">
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-INVITATION-email" onchange="saveNotifications()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Spielstatus -->
|
||||
<div class="notif-grid-row">
|
||||
<div>
|
||||
<div class="settings-row-label">Spielstatus</div>
|
||||
<div class="settings-row-desc">Karten, Aufgaben, Verifikationen, Einfrierungen und andere Spielereignisse</div>
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-GAME_STATE-inApp" onchange="saveNotifications()">
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-GAME_STATE-email" onchange="saveNotifications()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notfall -->
|
||||
<div class="notif-grid-row">
|
||||
<div>
|
||||
<div class="settings-row-label">Notfall</div>
|
||||
<div class="settings-row-desc">Notfall-Entsperrungen und dringende Meldungen</div>
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-EMERGENCY-inApp" onchange="saveNotifications()">
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-EMERGENCY-email" onchange="saveNotifications()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Freundschaftsanfragen -->
|
||||
<div class="notif-grid-row">
|
||||
<div>
|
||||
<div class="settings-row-label">Freundschaftsanfragen</div>
|
||||
<div class="settings-row-desc">Neue Freundschaftsanfragen von anderen Nutzern</div>
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-FRIENDREQUEST-inApp" checked disabled
|
||||
title="In-App-Benachrichtigungen für Freundschaftsanfragen sind immer aktiv">
|
||||
</div>
|
||||
<div class="notif-col-check">
|
||||
<input type="checkbox" id="notif-FRIENDREQUEST-email" onchange="saveNotifications()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Datenschutz -->
|
||||
<div class="settings-section" id="sec-datenschutz">
|
||||
<div class="settings-section-header" onclick="toggleSection('sec-datenschutz')">
|
||||
<span class="settings-section-title">🛡️ Datenschutz</span>
|
||||
<span class="settings-section-arrow">▸</span>
|
||||
</div>
|
||||
<div class="settings-section-body">
|
||||
|
||||
<!-- Grunddaten -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Grunddaten</div>
|
||||
<div class="settings-row-desc">Alter, Größe, Gewicht, Geschlecht, Neigung, Beziehungsstatus, Beschreibung</div>
|
||||
</div>
|
||||
<select id="sv-grunddaten" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Galerie -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Galerie</div>
|
||||
<div class="settings-row-desc">Fotos auf dem Profil</div>
|
||||
</div>
|
||||
<select id="sv-galerie" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Freunde -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Freundesliste</div>
|
||||
<div class="settings-row-desc">Wer kann sehen, wer deine Freunde sind</div>
|
||||
</div>
|
||||
<select id="sv-freunde" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Feed -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Feed / Posts</div>
|
||||
<div class="settings-row-desc">Posts auf dem Profil-Tab</div>
|
||||
</div>
|
||||
<select id="sv-feed" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Pinnwand -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Pinnwand</div>
|
||||
<div class="settings-row-desc">Einträge auf der Pinnwand</div>
|
||||
</div>
|
||||
<select id="sv-pinnwand" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- XP -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">XP-Punkte</div>
|
||||
<div class="settings-row-desc">Lockee-XP und Keyholder-XP</div>
|
||||
</div>
|
||||
<select id="sv-xp" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
<option value="NUR_ICH">Nur ich</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Lock-Historie -->
|
||||
<div class="settings-row">
|
||||
<div class="settings-row-info">
|
||||
<div class="settings-row-label">Lock-Historie</div>
|
||||
<div class="settings-row-desc">Abgeschlossene Locks und Keyholder-Aktivitäten</div>
|
||||
</div>
|
||||
<select id="sv-lockhistorie" onchange="doSave()">
|
||||
<option value="ALLE">Alle</option>
|
||||
<option value="NUR_FREUNDE">Nur Freunde</option>
|
||||
<option value="NUR_ICH">Nur ich</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr class="settings-separator">
|
||||
|
||||
<!-- Vorschau -->
|
||||
<div class="preview-row">
|
||||
<span>Profil-Vorschau – wie sieht mein Profil für andere aus?</span>
|
||||
<button onclick="openPreview('FREUND')">👥 Vorschau als Freund</button>
|
||||
<button onclick="openPreview('UNBEKANNT')" style="background:var(--color-secondary);color:var(--color-text);">👤 Vorschau als Fremder</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nickname Modal -->
|
||||
<div class="modal-backdrop" id="nameModal">
|
||||
<div class="modal">
|
||||
<h2>Nickname ändern</h2>
|
||||
<label for="newName">Neuer Nickname</label>
|
||||
<input type="text" id="newName" placeholder="Neuer Name" autocomplete="off">
|
||||
<div class="message" id="nameMessage"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="secondary" onclick="closeNameDialog()">Abbrechen</button>
|
||||
<button id="nameConfirmBtn" onclick="saveName()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Geburtsdatum Modal -->
|
||||
<div class="modal-backdrop" id="geburtsdatumModal">
|
||||
<div class="modal">
|
||||
<h2>Geburtsdatum ändern</h2>
|
||||
<label for="newGeburtsdatum">Neues Geburtsdatum</label>
|
||||
<input type="date" id="newGeburtsdatum" autocomplete="bday">
|
||||
<div class="message" id="geburtsdatumMessage"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="secondary" onclick="closeGeburtsdatumDialog()">Abbrechen</button>
|
||||
<button id="geburtsdatumConfirmBtn" onclick="saveGeburtsdatum()">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- E-Mail Modal -->
|
||||
<div class="modal-backdrop" id="emailModal">
|
||||
<div class="modal">
|
||||
<h2>E-Mail-Adresse ändern</h2>
|
||||
<label for="newEmail">Neue E-Mail-Adresse</label>
|
||||
<input type="email" id="newEmail" placeholder="neue@email.de" autocomplete="off">
|
||||
<div class="message" id="emailMessage"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="secondary" onclick="closeEmailDialog()">Abbrechen</button>
|
||||
<button id="emailConfirmBtn" onclick="requestEmailChange()">Bestätigungsmail senden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Konto löschen Modal -->
|
||||
<div class="modal-backdrop" id="deleteModal">
|
||||
<div class="modal">
|
||||
<h2>Konto löschen</h2>
|
||||
<p style="color:var(--color-muted);font-size:0.9rem;margin-bottom:0.75rem;">
|
||||
Dein Konto sowie alle gespeicherten Aufgaben und Toys werden unwiderruflich gelöscht.
|
||||
</p>
|
||||
<p style="color:var(--color-primary);font-size:0.85rem;">Dieser Vorgang kann nicht rückgängig gemacht werden.</p>
|
||||
<div class="message" id="deleteMessage"></div>
|
||||
<div class="modal-actions">
|
||||
<button class="secondary" onclick="closeDeleteDialog()">Abbrechen</button>
|
||||
<button id="deleteConfirmBtn" onclick="deleteAccount()" style="background:#c0392b;">Konto löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="save-toast" id="saveToast">✓ Gespeichert</div>
|
||||
|
||||
<script src="/js/sidebar.js"></script>
|
||||
<script src="/js/social-sidebar.js"></script>
|
||||
<script>
|
||||
let myUserId = null;
|
||||
let toastTimer = null;
|
||||
|
||||
function toggleSection(id) {
|
||||
const target = document.getElementById(id);
|
||||
const isOpen = target.classList.contains('open');
|
||||
document.querySelectorAll('.settings-section').forEach(s => s.classList.remove('open'));
|
||||
if (!isOpen) target.classList.add('open');
|
||||
}
|
||||
|
||||
// ── Grunddaten laden ──
|
||||
async function loadGrunddaten() {
|
||||
const res = await fetch('/login/me');
|
||||
if (res.status === 401) { window.location.href = '/login.html'; return; }
|
||||
if (!res.ok) return;
|
||||
const user = await res.json();
|
||||
myUserId = user.userId;
|
||||
document.getElementById('gd-name').textContent = user.name || '—';
|
||||
document.getElementById('gd-email').textContent = user.email || '—';
|
||||
document.getElementById('gd-geburtsdatum').textContent = formatGeburtsdatum(user.geburtsdatum);
|
||||
}
|
||||
|
||||
function formatGeburtsdatum(iso) {
|
||||
if (!iso) return '—';
|
||||
const [y, m, d] = iso.split('-');
|
||||
const today = new Date();
|
||||
const birth = new Date(iso);
|
||||
const age = today.getFullYear() - birth.getFullYear()
|
||||
- (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0);
|
||||
return `${d}.${m}.${y} (${age} Jahre)`;
|
||||
}
|
||||
|
||||
// ── Geburtsdatum Dialog ──
|
||||
function openGeburtsdatumDialog() {
|
||||
document.getElementById('newGeburtsdatum').value = '';
|
||||
hideModalMessage('geburtsdatumMessage');
|
||||
document.getElementById('geburtsdatumModal').classList.add('visible');
|
||||
document.getElementById('newGeburtsdatum').focus();
|
||||
}
|
||||
|
||||
function closeGeburtsdatumDialog() {
|
||||
document.getElementById('geburtsdatumModal').classList.remove('visible');
|
||||
}
|
||||
|
||||
async function saveGeburtsdatum() {
|
||||
const val = document.getElementById('newGeburtsdatum').value;
|
||||
if (!val) { showModalMessage('geburtsdatumMessage', 'Bitte ein Datum eingeben.', 'error'); return; }
|
||||
const today = new Date();
|
||||
const birth = new Date(val);
|
||||
const age = today.getFullYear() - birth.getFullYear()
|
||||
- (today < new Date(today.getFullYear(), birth.getMonth(), birth.getDate()) ? 1 : 0);
|
||||
if (age < 18) {
|
||||
showModalMessage('geburtsdatumMessage', 'Du musst mindestens 18 Jahre alt sein.', 'error');
|
||||
return;
|
||||
}
|
||||
const btn = document.getElementById('geburtsdatumConfirmBtn');
|
||||
btn.disabled = true; btn.textContent = 'Wird gespeichert…';
|
||||
hideModalMessage('geburtsdatumMessage');
|
||||
try {
|
||||
const res = await fetch('/user/me/geburtsdatum', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ geburtsdatum: val })
|
||||
});
|
||||
if (res.ok) {
|
||||
document.getElementById('gd-geburtsdatum').textContent = formatGeburtsdatum(val);
|
||||
closeGeburtsdatumDialog();
|
||||
showToast();
|
||||
} else if (res.status === 422) {
|
||||
showModalMessage('geburtsdatumMessage', 'Du musst mindestens 18 Jahre alt sein.', 'error');
|
||||
} else {
|
||||
showModalMessage('geburtsdatumMessage', `Fehler: HTTP ${res.status}`, 'error');
|
||||
}
|
||||
} catch (err) {
|
||||
showModalMessage('geburtsdatumMessage', 'Server nicht erreichbar.', 'error');
|
||||
console.error(err);
|
||||
} finally {
|
||||
btn.disabled = false; btn.textContent = 'Speichern';
|
||||
}
|
||||
}
|
||||
|
||||
// ── 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 res = await fetch('/user/me/name', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: newName })
|
||||
});
|
||||
if (res.ok) {
|
||||
document.getElementById('gd-name').textContent = newName;
|
||||
closeNameDialog();
|
||||
showToast();
|
||||
} else if (res.status === 409) {
|
||||
showModalMessage('nameMessage', 'Dieser Nickname ist bereits vergeben.', 'error');
|
||||
} else {
|
||||
showModalMessage('nameMessage', `Fehler: HTTP ${res.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 res = await fetch('/email-change', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ newEmail })
|
||||
});
|
||||
if (res.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 (res.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 ${res.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);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Konto löschen Dialog ──
|
||||
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 res = await fetch('/user/me', { method: 'DELETE' });
|
||||
if (res.ok) {
|
||||
window.location.href = '/login.html?accountDeleted=1';
|
||||
} else {
|
||||
showModalMessage('deleteMessage', `Fehler: HTTP ${res.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);
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
// Modal-Backdrop-Klick schließt Modals
|
||||
['nameModal','geburtsdatumModal','emailModal','deleteModal'].forEach(id => {
|
||||
document.getElementById(id).addEventListener('click', e => {
|
||||
if (e.target === document.getElementById(id)) document.getElementById(id).classList.remove('visible');
|
||||
});
|
||||
});
|
||||
document.getElementById('newName').addEventListener('keydown', e => { if (e.key === 'Enter') saveName(); });
|
||||
document.getElementById('newEmail').addEventListener('keydown', e => { if (e.key === 'Enter') requestEmailChange(); });
|
||||
|
||||
// ── Datenschutz laden ──
|
||||
async function loadPrivacy() {
|
||||
const meRes = await fetch('/login/me');
|
||||
if (!meRes.ok) return;
|
||||
const me = await meRes.json();
|
||||
myUserId = me.userId;
|
||||
|
||||
const profRes = await fetch('/social/users/' + myUserId);
|
||||
if (!profRes.ok) return;
|
||||
const profile = await profRes.json();
|
||||
|
||||
setValue('sv-grunddaten', profile.sichtbarkeitGrunddaten || 'ALLE');
|
||||
setValue('sv-galerie', profile.sichtbarkeitGalerie || 'ALLE');
|
||||
setValue('sv-freunde', profile.sichtbarkeitFreunde || 'ALLE');
|
||||
setValue('sv-feed', profile.sichtbarkeitFeed || 'ALLE');
|
||||
setValue('sv-pinnwand', profile.sichtbarkeitPinnwand || 'ALLE');
|
||||
setValue('sv-xp', profile.sichtbarkeitXp || 'ALLE');
|
||||
setValue('sv-lockhistorie', profile.sichtbarkeitLockhistorie || 'ALLE');
|
||||
}
|
||||
|
||||
function setValue(id, value) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.value = value;
|
||||
}
|
||||
|
||||
async function doSave() {
|
||||
const body = {
|
||||
sichtbarkeitGrunddaten: document.getElementById('sv-grunddaten').value,
|
||||
sichtbarkeitGalerie: document.getElementById('sv-galerie').value,
|
||||
sichtbarkeitFreunde: document.getElementById('sv-freunde').value,
|
||||
sichtbarkeitFeed: document.getElementById('sv-feed').value,
|
||||
sichtbarkeitPinnwand: document.getElementById('sv-pinnwand').value,
|
||||
sichtbarkeitXp: document.getElementById('sv-xp').value,
|
||||
sichtbarkeitLockhistorie: document.getElementById('sv-lockhistorie').value,
|
||||
};
|
||||
const res = await fetch('/user/me/privacy', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
if (res.ok) showToast();
|
||||
}
|
||||
|
||||
function showToast() {
|
||||
const t = document.getElementById('saveToast');
|
||||
t.classList.add('visible');
|
||||
clearTimeout(toastTimer);
|
||||
toastTimer = setTimeout(() => t.classList.remove('visible'), 2200);
|
||||
}
|
||||
|
||||
function openPreview(mode) {
|
||||
if (!myUserId) return;
|
||||
window.open('/benutzer.html?userId=' + myUserId + '&preview=' + mode, '_blank');
|
||||
}
|
||||
|
||||
// ── Benachrichtigungen laden ──
|
||||
async function loadNotifications() {
|
||||
const res = await fetch('/user/me/notifications');
|
||||
if (!res.ok) return;
|
||||
const data = await res.json();
|
||||
for (const cause of ['INVITATION', 'GAME_STATE', 'EMERGENCY']) {
|
||||
const pref = data[cause] || { inApp: true, email: false };
|
||||
const inApp = document.getElementById('notif-' + cause + '-inApp');
|
||||
const email = document.getElementById('notif-' + cause + '-email');
|
||||
if (inApp) inApp.checked = pref.inApp;
|
||||
if (email) email.checked = pref.email;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveNotifications() {
|
||||
const body = {};
|
||||
for (const cause of ['INVITATION', 'GAME_STATE', 'EMERGENCY']) {
|
||||
const inApp = document.getElementById('notif-' + cause + '-inApp');
|
||||
const email = document.getElementById('notif-' + cause + '-email');
|
||||
body[cause] = {
|
||||
inApp: inApp ? inApp.checked : true,
|
||||
email: email ? email.checked : false
|
||||
};
|
||||
}
|
||||
const res = await fetch('/user/me/notifications', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
if (res.ok) showToast();
|
||||
}
|
||||
|
||||
// Hash-Navigation: Sektion anhand URL-Fragment öffnen
|
||||
function openSectionFromHash() {
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (hash) {
|
||||
const el = document.getElementById(hash);
|
||||
if (el) el.classList.add('open');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Spiel Einstellungen – BDSM ──
|
||||
function applyCheckItems(groupId, values) {
|
||||
document.querySelectorAll(`#${groupId} .spiel-check-item input`).forEach(cb => {
|
||||
const checked = values.includes(cb.value);
|
||||
cb.checked = checked;
|
||||
cb.closest('.spiel-check-item').classList.toggle('is-checked', checked);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.spiel-check-item input').forEach(cb => {
|
||||
cb.addEventListener('change', () => {
|
||||
cb.closest('.spiel-check-item').classList.toggle('is-checked', cb.checked);
|
||||
saveBdsmDefaults();
|
||||
});
|
||||
});
|
||||
|
||||
async function loadBdsmDefaults() {
|
||||
const res = await fetch('/user/me/bdsm-defaults');
|
||||
if (!res.ok) return;
|
||||
const d = await res.json();
|
||||
applyCheckItems('bdsm-spieltmit', d.spieltMit || []);
|
||||
applyCheckItems('bdsm-rollen', d.rollen || []);
|
||||
applyCheckItems('bdsm-werkzeuge', d.werkzeuge || []);
|
||||
}
|
||||
|
||||
async function saveBdsmDefaults() {
|
||||
const get = groupId => [...document.querySelectorAll(`#${groupId} input:checked`)].map(cb => cb.value);
|
||||
await fetch('/user/me/bdsm-defaults', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
spieltMit: get('bdsm-spieltmit'),
|
||||
rollen: get('bdsm-rollen'),
|
||||
werkzeuge: get('bdsm-werkzeuge'),
|
||||
})
|
||||
});
|
||||
showToast();
|
||||
}
|
||||
|
||||
loadGrunddaten();
|
||||
loadPrivacy();
|
||||
loadNotifications();
|
||||
loadBdsmDefaults();
|
||||
openSectionFromHash();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user