diff --git a/.metadata/.lock_info b/.metadata/.lock_info index b28e3b6..c840751 100644 --- a/.metadata/.lock_info +++ b/.metadata/.lock_info @@ -1,5 +1,5 @@ -#Sat Mar 21 08:14:24 CET 2026 +#Sun Mar 22 19:48:43 CET 2026 display=\:0 host=Mario-Linux -process-id=23243 +process-id=106706 user=mario diff --git a/.metadata/.log b/.metadata/.log index 477d11e..512166c 100644 --- a/.metadata/.log +++ b/.metadata/.log @@ -9026,3 +9026,2929 @@ Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git at java.base/java.io.FileInputStream.(FileInputStream.java:106) at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) ... 68 more + +!ENTRY org.springframework.tooling.boot.ls 1 0 2026-03-21 17:46:26.993 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS +!SESSION 2026-03-22 08:51:00.969 ----------------------------------------------- +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-22 08:51:01.928 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 08:51:06.494 +!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-22 08:51:06.624 +!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-22 08:51:06.624 +!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-22 08:51:06.752 +!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-22 08:51:06.752 +!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.springframework.tooling.boot.ls 1 0 2026-03-22 08:53:15.931 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS +!SESSION 2026-03-22 08:53:18.790 ----------------------------------------------- +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-22 08:53:19.451 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 08:53:21.560 +!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-22 08:53:21.730 +!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-22 08:53:21.730 +!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-22 08:53:21.849 +!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-22 08:53:21.849 +!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.springframework.tooling.boot.ls 1 0 2026-03-22 08:53:46.753 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS +!SESSION 2026-03-22 08:53:48.178 ----------------------------------------------- +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 -data file:/home/mario/Workspaces/xxx-thegame/ + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 08:53:48.834 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 08:53:49.386 +!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-22 08:53:49.518 +!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-22 08:53:49.518 +!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-22 08:53:49.646 +!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-22 08:53:49.646 +!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.springframework.tooling.boot.ls 1 0 2026-03-22 08:54:12.239 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS + +!ENTRY org.eclipse.lsp4e 4 0 2026-03-22 08:54:15.526 +!MESSAGE Workspace is already closed or not ready yet. Consider tracking the org.eclipse.core.resources.IWorkspace service (using your favorite technique, e.g. Declarative Services, ServiceTracker, Blueprint, ...) instead of calling the static method here to prevent such issues! +!STACK 0 +java.util.concurrent.CompletionException: java.lang.IllegalStateException: Workspace is already closed or not ready yet. Consider tracking the org.eclipse.core.resources.IWorkspace service (using your favorite technique, e.g. Declarative Services, ServiceTracker, Blueprint, ...) instead of calling the static method here to prevent such issues! + at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:315) + at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:320) + at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:994) + at java.base/java.util.concurrent.CompletableFuture$UniExceptionally.tryFire(CompletableFuture.java:974) + at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) + at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2179) + at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleResponse(RemoteEndpoint.java:217) + at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:198) + at org.eclipse.lsp4e.LanguageServerWrapper.lambda$3(LanguageServerWrapper.java:449) + at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:189) + at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97) + at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:97) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +Caused by: java.lang.IllegalStateException: Workspace is already closed or not ready yet. Consider tracking the org.eclipse.core.resources.IWorkspace service (using your favorite technique, e.g. Declarative Services, ServiceTracker, Blueprint, ...) instead of calling the static method here to prevent such issues! + at org.eclipse.core.resources.ResourcesPlugin.getWorkspace(ResourcesPlugin.java:518) + at org.eclipse.lsp4e.LanguageServerWrapper.shutdown(LanguageServerWrapper.java:747) + at org.eclipse.lsp4e.LanguageServerWrapper.lambda$13(LanguageServerWrapper.java:522) + at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:990) + ... 14 more +!SESSION 2026-03-22 08:56:05.882 ----------------------------------------------- +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-22 08:56:06.864 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 08:56:10.276 +!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-22 08:56:10.421 +!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-22 08:56:10.421 +!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-22 08:56:10.542 +!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-22 08:56:10.542 +!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.lsp4e 2 0 2026-03-22 09:01:51.086 +!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.ui 4 0 2026-03-22 09:09:50.842 +!MESSAGE Unhandled event loop exception +!STACK 0 +org.eclipse.swt.SWTException: Widget is disposed + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.SWT.error(SWT.java:4836) + at org.eclipse.swt.widgets.Widget.error(Widget.java:598) + at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:513) + at org.eclipse.swt.widgets.Control.setFocus(Control.java:5457) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractAnnotationHover$AnnotationInformationControl.setFocus(AbstractAnnotationHover.java:226) + at org.eclipse.jface.text.AbstractInformationControlManager.showInformationControl(AbstractInformationControlManager.java:1282) + at org.eclipse.jface.internal.text.StickyHoverManager.showInformationControl(StickyHoverManager.java:272) + at org.eclipse.jface.internal.text.InformationControlReplacer.showInformationControl(InformationControlReplacer.java:153) + at org.eclipse.jface.text.AbstractInformationControlManager.internalShowInformationControl(AbstractInformationControlManager.java:1162) + at org.eclipse.jface.text.AbstractInformationControlManager.presentInformation(AbstractInformationControlManager.java:1147) + at org.eclipse.jface.text.AbstractInformationControlManager.setInformation(AbstractInformationControlManager.java:431) + at org.eclipse.jface.internal.text.InformationControlReplacer.computeInformation(InformationControlReplacer.java:117) + at org.eclipse.jface.text.AbstractInformationControlManager.doShowInformation(AbstractInformationControlManager.java:1127) + at org.eclipse.jface.text.AbstractInformationControlManager.showInformation(AbstractInformationControlManager.java:1116) + at org.eclipse.jface.internal.text.InformationControlReplacer.replaceInformationControl(InformationControlReplacer.java:104) + at org.eclipse.jface.text.AbstractInformationControlManager.replaceInformationControl(AbstractInformationControlManager.java:1306) + at org.eclipse.jface.text.AbstractHoverInformationControlManager.replaceInformationControl(AbstractHoverInformationControlManager.java:808) + at org.eclipse.jface.text.TextViewerHoverManager.replaceInformationControl(TextViewerHoverManager.java:304) + at org.eclipse.jface.text.AbstractHoverInformationControlManager$Closer.lambda$1(AbstractHoverInformationControlManager.java:297) + at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:40) + at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:132) + at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:5035) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4500) + 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) + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 09:13:39.465 +!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.ui.workbench 4 0 2026-03-22 09:25:15.105 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 10:21:59.199 +!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.lsp4e 2 0 2026-03-22 10:26:27.823 +!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.ui.workbench 4 0 2026-03-22 10:30:03.572 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 10:30:26.551 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 10:30:42.464 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 11:42:18.771 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 11:42:48.916 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 11:53:47.666 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 12:02:33.449 +!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.ui.workbench 4 0 2026-03-22 12:03:22.015 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 12:24:05.392 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 12:24:52.851 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 12:31:16.710 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 12:36:42.861 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 12:51:13.228 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 12:55:30.060 +!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.lsp4e 2 0 2026-03-22 12:56:18.352 +!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.ui.workbench 4 0 2026-03-22 13:02:02.207 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:05:32.333 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:05:54.774 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:06:02.902 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:30:18.868 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:38:34.441 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 13:41:16.153 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 13:49:00.559 +!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.lsp4e 2 0 2026-03-22 13:49:44.308 +!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.lsp4e 2 0 2026-03-22 13:57:01.185 +!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.lsp4e 2 0 2026-03-22 13:57:03.032 +!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.lsp4e 2 0 2026-03-22 13:57:05.638 +!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.lsp4e 2 0 2026-03-22 13:57:06.753 +!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.lsp4e 2 0 2026-03-22 13:57:15.060 +!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.lsp4e 2 0 2026-03-22 13:57:16.625 +!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.lsp4e 2 0 2026-03-22 13:57:20.246 +!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.lsp4e 2 0 2026-03-22 13:57:21.181 +!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.ui 4 2 2026-03-22 13:58:25.744 +!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.jdt.ui". +!STACK 0 +org.eclipse.core.runtime.AssertionFailedException: assertion failed: + at org.eclipse.core.runtime.Assert.isTrue(Assert.java:121) + at org.eclipse.core.runtime.Assert.isTrue(Assert.java:106) + at org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposalCore.(TypeChangeCorrectionProposalCore.java:178) + at org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposal.(TypeChangeCorrectionProposal.java:45) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchSubProcessor.createChangeConstructorTypeProposal(TypeMismatchSubProcessor.java:143) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchSubProcessor.createChangeConstructorTypeProposal(TypeMismatchSubProcessor.java:1) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchBaseSubProcessor.collectChangeSenderTypeProposals(TypeMismatchBaseSubProcessor.java:334) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.doEqualNumberOfParameters(UnresolvedElementsBaseSubProcessor.java:2039) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.addParameterMissmatchProposals(UnresolvedElementsBaseSubProcessor.java:1752) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.collectMethodProposals(UnresolvedElementsBaseSubProcessor.java:1478) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsSubProcessor.getMethodProposals(UnresolvedElementsSubProcessor.java:155) + at org.eclipse.jdt.internal.ui.text.correction.QuickFixProcessor.process(QuickFixProcessor.java:407) + at org.eclipse.jdt.internal.ui.text.correction.QuickFixProcessor.getCorrections(QuickFixProcessor.java:370) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionCollector.safeRun(JavaCorrectionProcessor.java:381) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionProcessorAccess.run(JavaCorrectionProcessor.java:341) + at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionProcessorAccess.process(JavaCorrectionProcessor.java:336) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor.collectCorrections(JavaCorrectionProcessor.java:465) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.getJavaAnnotationFixes(ProblemHover.java:115) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.computeCompletionProposals(ProblemHover.java:89) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.(ProblemHover.java:74) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover.createAnnotationInfo(ProblemHover.java:178) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractAnnotationHover.getHoverInfo2(AbstractAnnotationHover.java:957) + 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.ui 4 2 2026-03-22 13:58:27.884 +!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.jdt.ui". +!STACK 0 +org.eclipse.core.runtime.AssertionFailedException: assertion failed: + at org.eclipse.core.runtime.Assert.isTrue(Assert.java:121) + at org.eclipse.core.runtime.Assert.isTrue(Assert.java:106) + at org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposalCore.(TypeChangeCorrectionProposalCore.java:178) + at org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposal.(TypeChangeCorrectionProposal.java:45) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchSubProcessor.createChangeConstructorTypeProposal(TypeMismatchSubProcessor.java:143) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchSubProcessor.createChangeConstructorTypeProposal(TypeMismatchSubProcessor.java:1) + at org.eclipse.jdt.internal.ui.text.correction.TypeMismatchBaseSubProcessor.collectChangeSenderTypeProposals(TypeMismatchBaseSubProcessor.java:334) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.doEqualNumberOfParameters(UnresolvedElementsBaseSubProcessor.java:2039) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.addParameterMissmatchProposals(UnresolvedElementsBaseSubProcessor.java:1752) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsBaseSubProcessor.collectMethodProposals(UnresolvedElementsBaseSubProcessor.java:1478) + at org.eclipse.jdt.internal.ui.text.correction.UnresolvedElementsSubProcessor.getMethodProposals(UnresolvedElementsSubProcessor.java:155) + at org.eclipse.jdt.internal.ui.text.correction.QuickFixProcessor.process(QuickFixProcessor.java:407) + at org.eclipse.jdt.internal.ui.text.correction.QuickFixProcessor.getCorrections(QuickFixProcessor.java:370) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionCollector.safeRun(JavaCorrectionProcessor.java:381) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionProcessorAccess.run(JavaCorrectionProcessor.java:341) + at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:47) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor$SafeCorrectionProcessorAccess.process(JavaCorrectionProcessor.java:336) + at org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor.collectCorrections(JavaCorrectionProcessor.java:465) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.getJavaAnnotationFixes(ProblemHover.java:115) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.computeCompletionProposals(ProblemHover.java:89) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover$ProblemInfo.(ProblemHover.java:74) + at org.eclipse.jdt.internal.ui.text.java.hover.ProblemHover.createAnnotationInfo(ProblemHover.java:178) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractAnnotationHover.getHoverInfo2(AbstractAnnotationHover.java:957) + 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.lsp4e 2 0 2026-03-22 14:09:12.341 +!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.lsp4e 2 0 2026-03-22 14:10:06.585 +!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.ui 4 0 2026-03-22 16:03:03.669 +!MESSAGE NullPointerException computing hover information in TimeLockService.java at offset 10345 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 260 : +----- + boolean valid = true; + if (lock.isEmergencyAutoUnlocked()) { + valid = false; + LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); + } + if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + Set verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream() + .filter(verification -> isValid(verification)) + .map(|verification -> verification.getVerificationTime().toLocalDate()).collect(Collectors.toSet()); +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 16:06:52.510 +!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.lsp4e 2 0 2026-03-22 17:29:09.129 +!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.ui 4 0 2026-03-22 17:31:29.925 +!MESSAGE NullPointerException computing hover information in TimeLockService.java at offset 10438 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 265 : +----- + LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); + } + if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + List verifications = null; + if (lock.getKeyholder() != null) { + + } else { + verificationRepository.findByLockId(lock.getLockId()).stream() + .filter(|verification -> isValid(verification)) +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.jdt.ui 4 0 2026-03-22 17:37:40.105 +!MESSAGE NullPointerException computing hover information in TimeLockService.java at offset 10488 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 266 : +----- + } + if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + List verifications = null; + if (lock.getKeyholder() != null) { + + } else { + verificationRepository.findByLockId(lock.getLockId()). + stream().filter(verification -> verification.isValid()) + .map(|verification -> verification.getVerificationTime() +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.jdt.ui 4 0 2026-03-22 17:40:41.492 +!MESSAGE NullPointerException computing hover information in TimeLockService.java at offset 10724 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 268 : +----- + } + if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + List verifications = null; + if (lock.getKeyholder() != null) { + + } else { + verifications = verificationRepository.findByLockId(lock.getLockId()).stream() + .filter(verification -> verification.isValid()) + .map(|verification -> verification.getCreatedAt().toLocalDate()).collect(Collectors.toSet()); +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 17:40:46.398 +!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.lsp4e 2 0 2026-03-22 17:44:16.900 +!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.ui 4 0 2026-03-22 17:44:18.660 +!MESSAGE NullPointerException computing hover information in TimeLockService.java at offset 10931 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 271 : +----- + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + Set verifications = null; + if (lock.getKeyholder() != null) { + verifications = keyholderVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(verification -> verification.isValid()) + .map(verification -> verification.getVerificationDate()).collect(Collectors.toSet()); + + } else { + verifications = verificationRepository.findByLockId(lock.getLockId()).stream() + .filter(|verification -> verification.isValid()) +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 17:46:55.579 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.lsp4e 2 0 2026-03-22 17:55:44.236 +!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.springframework.tooling.boot.ls 1 0 2026-03-22 18:02:19.168 +!MESSAGE DelegatingStreamConnectionProvider - Stopping Boot LS +!SESSION 2026-03-22 19:47:47.271 ----------------------------------------------- +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-22 19:47:47.910 +!MESSAGE Activated before the state location was initialized. Retry after the state location is initialized. + +!ENTRY ch.qos.logback.classic 1 0 2026-03-22 19:48:43.354 +!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-22 19:48:43.457 +!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-22 19:48:43.457 +!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-22 19:48:43.592 +!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-22 19:48:43.592 +!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.lsp4e 2 0 2026-03-22 19:54:33.684 +!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.ui 4 0 2026-03-22 19:59:56.855 +!MESSAGE NullPointerException computing hover information in CardLockService.java at offset 5127 +!STACK 0 +java.lang.NullPointerException: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.TypeBinding.signableName()" because "binding.type" is null + at org.eclipse.jdt.internal.core.SelectionRequestor.acceptLocalVariable(SelectionRequestor.java:422) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.selectFrom(SelectionEngine.java:1353) + at org.eclipse.jdt.internal.codeassist.SelectionEngine.select(SelectionEngine.java:1043) + at org.eclipse.jdt.internal.core.Openable.codeSelect(Openable.java:171) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:476) + at org.eclipse.jdt.internal.core.CompilationUnit.codeSelect(CompilationUnit.java:466) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:122) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.lambda$0(JavadocHover.java:708) + at org.eclipse.jdt.internal.core.JavaModelManager.cacheZipFiles(JavaModelManager.java:5709) + at org.eclipse.jdt.internal.core.JavaModelManager.callReadOnly(JavaModelManager.java:5698) + at org.eclipse.jdt.core.JavaCore.callReadOnly(JavaCore.java:6211) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.internalGetHoverInfo(JavadocHover.java:708) + at org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.getHoverInfo2(JavadocHover.java:704) + at org.eclipse.lsp4e.jdt.LSJavaHoverProvider.lambda$0(LSJavaHoverProvider.java:64) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804) + at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796) + at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) + at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) + at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) + at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) + at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) + Suppressed: java.lang.Throwable: Source line 129 : +----- + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + Set verifications = null; + if (lock.getKeyholder() != null) { + verifications = keyholderVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(verification -> verification.isValid()) + .map(verification -> verification.getVerificationDate()).collect(Collectors.toSet()); + + } else { + verifications = communityVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(verification -> verification.isValid()) + .map(|verification -> verification.getVerificationDate()).collect(Collectors.toSet()); +----- + at org.eclipse.jdt.internal.ui.util.SelectionUtil.logException(SelectionUtil.java:157) + at org.eclipse.jdt.internal.ui.text.java.hover.AbstractJavaEditorTextHover.getJavaElementsAt(AbstractJavaEditorTextHover.java:124) + ... 14 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 20:09:49.269 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 20:13:11.923 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 22:16:36.997 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.ui.workbench 4 0 2026-03-22 22:16:55.406 +!MESSAGE Dynamic menu contribution 'DynamicContributionItems(id=org.eclipse.terminal.connector.local.LocalLauncherDynamicContributionItems, visible=true)' threw an unexpected exception +!STACK 0 +org.eclipse.swt.SWTException: i/o error (java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden)) + at org.eclipse.swt.SWT.error(SWT.java:4950) + at org.eclipse.swt.SWT.error(SWT.java:4865) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:207) + at org.eclipse.swt.graphics.ImageLoader.load(ImageLoader.java:198) + at org.eclipse.terminal.view.ui.internal.local.showin.ExternalExecutablesUtils.loadImage(ExternalExecutablesUtils.java:38) + at org.eclipse.terminal.view.ui.internal.local.showin.DynamicContributionItems.getContributionItems(DynamicContributionItems.java:76) + at org.eclipse.ui.actions.CompoundContributionItem.getContributionItemsToFill(CompoundContributionItem.java:83) + at org.eclipse.ui.actions.CompoundContributionItem.fill(CompoundContributionItem.java:57) + at org.eclipse.ui.internal.menus.DynamicMenuContributionItem.fill(DynamicMenuContributionItem.java:194) + at org.eclipse.jface.action.MenuManager.doItemFill(MenuManager.java:727) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:804) + at org.eclipse.jface.action.MenuManager.update(MenuManager.java:671) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.scheduleManagerUpdate(MenuManagerRenderer.java:1149) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRenderer.subscribeUIElementTopicVisible(MenuManagerRenderer.java:211) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:58) + at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:183) + at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:136) + at org.eclipse.swt.widgets.Display.syncExec(Display.java:5950) + at org.eclipse.e4.ui.workbench.swt.DisplayUISynchronize.syncExec(DisplayUISynchronize.java:34) + at org.eclipse.e4.ui.internal.di.UIEventObjectSupplier$UIEventHandler.handleEvent(UIEventObjectSupplier.java:65) + at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:206) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:201) + at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) + at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) + at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:151) + at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:132) + at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:73) + at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:48) + at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:55) + at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:61) + at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:424) + at org.eclipse.e4.ui.model.application.ui.impl.UIElementImpl.setVisible(UIElementImpl.java:365) + at org.eclipse.e4.ui.workbench.renderers.swt.ContributionRecord.updateVisibility(ContributionRecord.java:110) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:169) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerRendererFilter.updateElementVisibility(MenuManagerRendererFilter.java:179) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.showMenu(MenuManagerShowProcessor.java:243) + at org.eclipse.e4.ui.workbench.renderers.swt.MenuManagerShowProcessor.menuAboutToHide(MenuManagerShowProcessor.java:111) + at org.eclipse.jface.internal.MenuManagerEventHelper.showEventPostHelper(MenuManagerEventHelper.java:89) + at org.eclipse.jface.action.MenuManager.handleAboutToShow(MenuManager.java:467) + at org.eclipse.jface.action.MenuManager$2.menuShown(MenuManager.java:493) + at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:297) + at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:91) + at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5845) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1656) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1682) + at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1661) + at org.eclipse.swt.widgets.Menu._setVisible(Menu.java:290) + at org.eclipse.swt.widgets.Display.runPopups(Display.java:5102) + at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4488) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1160) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1051) + at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:153) + at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:684) + at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339) + at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:583) + at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:173) + at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:185) + at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:219) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:149) + at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:115) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:467) + at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:298) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:615) + at org.eclipse.equinox.launcher.Main.basicRun(Main.java:563) + at org.eclipse.equinox.launcher.Main.run(Main.java:1415) + at org.eclipse.equinox.launcher.Main.main(Main.java:1387) +Caused by: java.io.FileNotFoundException: C:\Program Files\Git\mingw64\share\git\git-for-windows.ico (Datei oder Verzeichnis nicht gefunden) + at java.base/java.io.FileInputStream.open0(Native Method) + at java.base/java.io.FileInputStream.open(FileInputStream.java:213) + at java.base/java.io.FileInputStream.(FileInputStream.java:152) + at java.base/java.io.FileInputStream.(FileInputStream.java:106) + at org.eclipse.swt.graphics.ImageLoader.loadByZoom(ImageLoader.java:204) + ... 68 more + +!ENTRY org.eclipse.jface 2 0 2026-03-22 22:20:17.677 +!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. +!SUBENTRY 1 org.eclipse.jface 2 0 2026-03-22 22:20:17.677 +!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) diff --git a/.metadata/.plugins/org.eclipse.buildship.core/gradle/versions.json b/.metadata/.plugins/org.eclipse.buildship.core/gradle/versions.json index d647ba8..97e1b24 100644 --- a/.metadata/.plugins/org.eclipse.buildship.core/gradle/versions.json +++ b/.metadata/.plugins/org.eclipse.buildship.core/gradle/versions.json @@ -1,24 +1,7 @@ [ { - "version" : "9.6.0-20260321035213+0000", - "buildTime" : "20260321035213+0000", - "commitId" : "cc9b7c946cf24a57b2119d39f1a33cf5493ea930", - "current" : false, - "snapshot" : true, - "nightly" : true, - "releaseNightly" : false, - "activeRc" : false, - "rcFor" : "", - "milestoneFor" : "", - "broken" : false, - "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-bin.zip", - "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-bin.zip.sha256", - "checksum" : "e533696ad1e80c2878ed39c18b5252ac7e0bad6394ead2b93663656cd6591059", - "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260321035213+0000-wrapper.jar.sha256", - "wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a" -}, { - "version" : "9.5.0-20260321014114+0000", - "buildTime" : "20260321014114+0000", - "commitId" : "e7d13113f033cc35f5b8c7e0eeb88906259a1410", + "version" : "9.5.0-20260322013634+0000", + "buildTime" : "20260322013634+0000", + "commitId" : "01db0eb99f616dd415a084ffcce4cb2c185d5a2a", "current" : false, "snapshot" : true, "nightly" : false, @@ -27,10 +10,27 @@ "rcFor" : "", "milestoneFor" : "", "broken" : false, - "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-bin.zip", - "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-bin.zip.sha256", - "checksum" : "eeb50f4468d73f74a68fe62a16d371ccc7c54088d7d2f672adb12c4bce4104d5", - "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260321014114+0000-wrapper.jar.sha256", + "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260322013634+0000-bin.zip", + "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260322013634+0000-bin.zip.sha256", + "checksum" : "3e8a6689594399f81087ad962b1c489e0ae57201af0c6c00ea63d9d07e48506e", + "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.5.0-20260322013634+0000-wrapper.jar.sha256", + "wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a" +}, { + "version" : "9.6.0-20260322000231+0000", + "buildTime" : "20260322000231+0000", + "commitId" : "d63be1b9cd4d937f4a9f5cf7ee78eec20fe5354e", + "current" : false, + "snapshot" : true, + "nightly" : true, + "releaseNightly" : false, + "activeRc" : false, + "rcFor" : "", + "milestoneFor" : "", + "broken" : false, + "downloadUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260322000231+0000-bin.zip", + "checksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260322000231+0000-bin.zip.sha256", + "checksum" : "80f3af587bc824675e2a5617c7f30a3b8e4888746d486bc8b3517ebf84f028a9", + "wrapperChecksumUrl" : "https://services.gradle.org/distributions-snapshots/gradle-9.6.0-20260322000231+0000-wrapper.jar.sha256", "wrapperChecksum" : "f307680272dffdb8e636f1169adfbf693513005c80aa06e8d381f20390a06e6a" }, { "version" : "9.4.1", diff --git a/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame b/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame index f7cb53b..5a35a97 100644 --- a/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame +++ b/.metadata/.plugins/org.eclipse.buildship.core/project-preferences/xxxthegame @@ -1,5 +1,5 @@ # -#Sat Mar 21 08:14:16 CET 2026 +#Sun Mar 22 18:02:19 CET 2026 buildDir=build buildScriptPath=build.gradle.kts classpath=\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n\t\n\t\t\n\t\t\n\t\n\n\n diff --git a/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers b/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers index 462ee1f..ea59e5c 100644 Binary files a/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers and b/.metadata/.plugins/org.eclipse.core.resources/.projects/xxxthegame/.markers differ diff --git a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources index 8899cb4..6dd884b 100644 Binary files a/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources and b/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources differ diff --git a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi index 02f3ef2..298897d 100644 --- a/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi +++ b/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi @@ -1,8 +1,8 @@ - - + + activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration - + @@ -11,9 +11,9 @@ topLevel shellMaximized - - - + + + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation persp.actionSet:org.eclipse.ui.cheatsheets.actionSet @@ -84,139 +84,144 @@ persp.editorOnboardingCommand:Show Key Assist$$$Shift+Ctrl+L persp.editorOnboardingCommand:New$$$Ctrl+N persp.editorOnboardingCommand:Open Type$$$Shift+Ctrl+T - - - - + + + + org.eclipse.e4.primaryNavigationStack - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:Java - - + + View categoryTag:Other - - + + View categoryTag:Git - - - - + + + + org.eclipse.e4.secondaryNavigationStack Minimized - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Mylyn - + View categoryTag:Java - + View categoryTag:Ant - + org.eclipse.e4.secondaryDataStack Oomph Gradle Debug Java - + Version Control (Team) + View categoryTag:General - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Terminal - + View categoryTag:Gradle - + View categoryTag:Gradle - + View categoryTag:Debug busy - + + View + categoryTag:Java + + View categoryTag:Oomph NoRestore - + View - categoryTag:Java + categoryTag:Version Control (Team) - + persp.actionSet:org.eclipse.mylyn.tasks.ui.navigation persp.actionSet:org.eclipse.ui.cheatsheets.actionSet @@ -265,100 +270,100 @@ persp.editorOnboardingCommand:Step Over$$$F6 persp.editorOnboardingCommand:Step Return$$$F7 persp.editorOnboardingCommand:Resume$$$F8 - - + + org.eclipse.e4.primaryNavigationStack - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:Java active - + View categoryTag:Java - + View categoryTag:Java - - - - + + + + org.eclipse.e4.secondaryNavigationStack - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Ant - - + + View categoryTag:General - + View categoryTag:General - + View categoryTag:Debug - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Terminal - + View categoryTag:Debug - + View categoryTag:General @@ -367,2857 +372,2700 @@ - - + + View categoryTag:Help - + View categoryTag:General - + View categoryTag:Help - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:Help - - + + EditorStack org.eclipse.e4.primaryDataStack active - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - - Editor - removeOnHide - org.eclipse.jdt.ui.CompilationUnitEditor - - - + noFocus + + Editor removeOnHide org.eclipse.jdt.ui.CompilationUnitEditor active + + + Editor + removeOnHide + org.eclipse.jdt.ui.CompilationUnitEditor + + + + Editor + removeOnHide + org.eclipse.jdt.ui.CompilationUnitEditor + + + + Editor + removeOnHide + org.eclipse.jdt.ui.CompilationUnitEditor + + + + Editor + removeOnHide + org.eclipse.jdt.ui.CompilationUnitEditor + + + + Editor + removeOnHide + org.eclipse.jdt.ui.CompilationUnitEditor + - + View categoryTag:Java - + ViewMenu menuContribution:menu - + - + View categoryTag:Java - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:General - highlighted - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Mylyn - + View categoryTag:Terminal - + View categoryTag:Java - + View categoryTag:Git - + View categoryTag:Java - + ViewMenu menuContribution:menu - + - + View categoryTag:Other - + ViewMenu menuContribution:menu - + - + View categoryTag:Ant - + View categoryTag:Gradle - + ViewMenu menuContribution:menu - + - + View categoryTag:Gradle - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - busy - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + View categoryTag:General - + View categoryTag:General - + - + View categoryTag:Debug - + ViewMenu menuContribution:menu - + - + + + + + View + categoryTag:Java + + ViewMenu + menuContribution:menu + + + + View categoryTag:Oomph NoRestore - + ViewMenu menuContribution:menu - + - - - - + + + + View - categoryTag:Java - + categoryTag:Version Control (Team) + ViewMenu menuContribution:menu - + - - + + toolbarSeparator - + - + Draggable - + - + toolbarSeparator - + - + Draggable - - + + - + toolbarSeparator - + - + Draggable - + Draggable - + Draggable - + Draggable - + toolbarSeparator - + - + Draggable - + - + Draggable - + toolbarSeparator - + - + toolbarSeparator - + - + Draggable - + stretch SHOW_RESTORE_MENU - + Draggable HIDEABLE SHOW_RESTORE_MENU - - + + stretch - + Draggable - + Draggable - - + + TrimStack Draggable - + TrimStack Draggable - - + + TrimStack Draggable - + TrimStack Draggable - + TrimStack Draggable - + TrimStack Draggable - - - - - - - - - - - - - + + + + + + + + + + + + + platform:gtk - - - - + + + + platform:gtk - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - + + + + + - - + + - - - - - - - - - + + + + + + + + + - - + + - - - + + + - - - - - + + + + + - - + + - - - + + + - - - + + + - - - - - - - - + + + + + + + + platform:gtk - - - - - + + + + + - - + + - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - + + + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + - - - - + + + + - - - - - - - - + + + + + + + + - - + + - - - - - - + + + + + + - - - - - - + + + + + + - - + + - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Editor removeOnHide - + View categoryTag:Ant - + View categoryTag:Gradle - + View categoryTag:Gradle - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Debug - + View categoryTag:Java - + View categoryTag:Git - + View categoryTag:Git - + View categoryTag:Git - + View categoryTag:Git NoRestore - + View categoryTag:Git - + View categoryTag:Help - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Debug - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java Browsing - + View categoryTag:Java - + View categoryTag:General - + View categoryTag:Java - + View categoryTag:Java - + View categoryTag:Language Servers - + View categoryTag:Language Servers - + View categoryTag:Language Servers - + View categoryTag:Maven - + View categoryTag:Maven - + View categoryTag:Maven - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Mylyn - + View categoryTag:Oomph - + View categoryTag:Oomph NoRestore - + View categoryTag:Plug-in Development - + View categoryTag:General - + View categoryTag:Version Control (Team) - + View categoryTag:Version Control (Team) - + View categoryTag:Terminal - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Help - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:General - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Docker - + View categoryTag:Spring - + View categoryTag:Spring - + View categoryTag:Spring - - + + glue move_after:PerspectiveSpacer SHOW_RESTORE_MENU - + move_after:Spacer Glue HIDEABLE SHOW_RESTORE_MENU - + glue move_after:SearchField SHOW_RESTORE_MENU - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index b/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index index 292f521..4bd8fee 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index and b/.metadata/.plugins/org.eclipse.jdt.core/1865797976.index differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache index 0a488d3..68bf8e8 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache and b/.metadata/.plugins/org.eclipse.jdt.core/externalFilesCache differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps index 0ede2b3..f9d28f6 100644 Binary files a/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps and b/.metadata/.plugins/org.eclipse.jdt.core/externalLibsTimeStamps differ diff --git a/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt index 3bcd953..79b1cc8 100644 --- a/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt +++ b/.metadata/.plugins/org.eclipse.jdt.core/savedIndexNames.txt @@ -11,6 +11,7 @@ INDEX VERSION 1.134+/home/mario/Workspaces/xxx-thegame/.metadata/.plugins/org.ec 134995224.index 4025319337.index 900586112.index +9341915.index 2929476459.index 2065500052.index 3051047092.index diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml index 11e3ba5..fea6878 100644 --- a/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml +++ b/.metadata/.plugins/org.eclipse.jdt.ui/OpenTypeHistory.xml @@ -4,4 +4,13 @@ + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml index 82a39df..4ca960e 100644 --- a/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml +++ b/.metadata/.plugins/org.eclipse.jdt.ui/QualifiedTypeNameHistory.xml @@ -1,28 +1,63 @@ - - - - - - - - - - + + + + - - - - - - - - - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml index bf1f8c4..8245628 100644 --- a/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml +++ b/.metadata/.plugins/org.eclipse.jdt.ui/dialog_settings.xml @@ -90,4 +90,21 @@ +
+ + + + + +
+
+ + + + + +
+
+ +
diff --git a/.metadata/.plugins/org.eclipse.m2e.logback/0.log b/.metadata/.plugins/org.eclipse.m2e.logback/0.log index 0999536..755f4ad 100644 --- a/.metadata/.plugins/org.eclipse.m2e.logback/0.log +++ b/.metadata/.plugins/org.eclipse.m2e.logback/0.log @@ -35,3 +35,8 @@ 2026-03-21 08:03:15,921 [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-21 08:10:51,183 [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-21 08:14:27,502 [Worker-8: 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-22 08:51:09,620 [Worker-8: 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-22 08:53:24,304 [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. +2026-03-22 08:53:52,098 [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-22 08:56:13,330 [Worker-5: 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-22 19:48:46,352 [Worker-8: Loading available Gradle versions] INFO o.e.b.c.i.u.g.PublishedGradleVersions - Gradle version information cache is up-to-date. Trying to read. diff --git a/.metadata/version.ini b/.metadata/version.ini index 0b09061..f474036 100644 --- a/.metadata/version.ini +++ b/.metadata/version.ini @@ -1,3 +1,3 @@ -#Sat Mar 21 08:14:24 CET 2026 +#Sun Mar 22 19:48:43 CET 2026 org.eclipse.core.runtime=2 org.eclipse.platform=4.39.0.v20260226-0420 diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameService.java index cf6a3c2..934f207 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/bdsm/BdsmGameService.java @@ -136,7 +136,7 @@ public class BdsmGameService { newLock.setTasks(template.getTasks()); newLock.setRequiresVerification(template.isRequiresVerification()); newLock.setTestLock(false); - newLock.setTaskCardMode(template.getTaskCardMode()); + newLock.setTaskMode(template.getTaskMode()); int codeLines = template.getUnlockCodeLength() != null ? template.getUnlockCodeLength() : 5; newLock.setUnlockCodeLength(codeLines); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java index 8fe9a0f..d8f8995 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockController.java @@ -37,25 +37,24 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import de.oaa.xxx.games.chastity.common.CodeCreator; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationEntity; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteEntity; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationEntity; import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; -import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceEntity; import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; import de.oaa.xxx.games.chastity.lockee.LockeeInvitationEntity; import de.oaa.xxx.games.chastity.lockee.LockeeInvitationRepository; 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.games.chastity.tasks.TaskMode; import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryRepository; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; -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.games.chastity.vote.CommunityTaskVoteEntity; -import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository; import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; @@ -66,8 +65,8 @@ public class CardLockController { private final CardlockRepository cardlockRepository; private final UserRepository userRepository; private final KeyholderInvitationRepository invitationRepository; - private final VerificationRepository verificationRepository; - private final VerificationVoteRepository verificationVoteRepository; + private final CommunityVerificationRepository verificationRepository; + private final CommunityVerificationVoteRepository verificationVoteRepository; private final KeyholderNotificationRepository keyholderNotificationRepository; private final LockeeInvitationRepository lockeeInvitationRepository; private final AssignedTaskRepository assignedTaskRepository; @@ -81,16 +80,20 @@ public class CardLockController { @Value("${app.base-url:http://localhost:8080}") private String baseUrl; - public CardLockController(CardlockRepository cardlockRepository, UserRepository userRepository, - KeyholderInvitationRepository invitationRepository, VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, + public CardLockController(CardlockRepository cardlockRepository, + UserRepository userRepository, + KeyholderInvitationRepository invitationRepository, + CommunityVerificationRepository verificationRepository, + CommunityVerificationVoteRepository verificationVoteRepository, KeyholderNotificationRepository keyholderNotificationRepository, - LockeeInvitationRepository lockeeInvitationRepository, AssignedTaskRepository assignedTaskRepository, + LockeeInvitationRepository lockeeInvitationRepository, + AssignedTaskRepository assignedTaskRepository, KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, CommunityTaskVoteRepository communityTaskVoteRepository, - UnlockCodeHistoryRepository unlockCodeHistoryRepository, UnlockCodeHistoryService unlockCodeHistoryService, - - SystemMessageService systemMessageService, CardLockServiceFactory cardLockServiceFactory) { + UnlockCodeHistoryRepository unlockCodeHistoryRepository, + UnlockCodeHistoryService unlockCodeHistoryService, + SystemMessageService systemMessageService, + CardLockServiceFactory cardLockServiceFactory) { this.cardlockRepository = cardlockRepository; this.userRepository = userRepository; this.invitationRepository = invitationRepository; @@ -111,7 +114,7 @@ public class CardLockController { List initialCards, Integer pickEveryMinute, boolean accumulatePicks, boolean showRemainingCards, LocalDateTime latestOpeningtime, Integer hygineOpeningDurationMinutes, Integer hygineOpeningEveryMinites, List tasks, boolean requiresVerification, boolean testLock, Integer unlockCodeLines, - String taskCardMode) { + TaskMode taskMode) { } private static final SecureRandom RNG = new SecureRandom(); @@ -161,7 +164,7 @@ public class CardLockController { lock.setTasks(req.tasks() != null ? req.tasks() : List.of()); lock.setRequiresVerification(req.requiresVerification()); lock.setTestLock(false); - lock.setTaskCardMode(req.taskCardMode() != null ? req.taskCardMode() : "RANDOM"); + lock.setTaskMode(req.taskMode() != null ? req.taskMode() : TaskMode.RANDOM); // startTime, unlockCode, unlockCodeLines left null until lockee accepts cardlockRepository.save(lock); @@ -204,7 +207,7 @@ public class CardLockController { lock.setTasks(req.tasks() != null ? req.tasks() : List.of()); lock.setRequiresVerification(req.requiresVerification()); lock.setTestLock(req.testLock()); - lock.setTaskCardMode(req.taskCardMode() != null ? req.taskCardMode() : "RANDOM"); + lock.setTaskMode(req.taskMode() != null ? req.taskMode() : TaskMode.RANDOM); lock.setUnlockCodeLength(codeLines); lock.setUnlockCode(unlockCode); @@ -267,34 +270,7 @@ public class CardLockController { if (dto == null) return ResponseEntity.status(409).body(Map.of("error", "Keine Karte verfügbar")); - // Task-Karte in nicht-zufälligem Modus → Entscheidung delegieren - String taskPending = null; - if (dto.card() == CardEnum.TASK && !"RANDOM".equals(l.getTaskCardMode()) && l.getTasks() != null - && !l.getTasks().isEmpty()) { - - if ("KEYHOLDER".equals(l.getTaskCardMode()) && l.getKeyholder() != null) { - KeyholderTaskChoiceEntity choice = new KeyholderTaskChoiceEntity(); - choice.setLockId(l.getLockId()); - choice.setCreatedAt(LocalDateTime.now()); - choice.setStatus("PENDING"); - keyholderTaskChoiceRepository.save(choice); - 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", de.oaa.xxx.social.entity.MessageCause.GAME_STATE)); - taskPending = "KEYHOLDER"; - - } else if ("COMMUNITY".equals(l.getTaskCardMode())) { - CommunityTaskVoteEntity vote = new CommunityTaskVoteEntity(); - vote.setLockId(l.getLockId()); - vote.setCreatedAt(LocalDateTime.now()); - vote.setExpiresAt(LocalDateTime.now().plusHours(1)); - vote.setStatus("ACTIVE"); - vote.setTestLock(l.isTestLock()); - communityTaskVoteRepository.save(vote); - taskPending = l.isTestLock() ? "RANDOM" : "COMMUNITY"; - } - } + String taskPending = (dto.card() == CardEnum.TASK) ? service.getPendingTaskMode() : null; Map result = new HashMap<>(); result.put("card", dto.card().name()); @@ -455,10 +431,10 @@ public class CardLockController { result.put("totalCards", totalCards); result.put("openPicks", l.getOpenPicks() != null ? l.getOpenPicks() : 0); result.put("nextCardIn", l.getNextCardIn() != null ? l.getNextCardIn().toString() : ""); - result.put("frozenUntill", l.getFrozenUntill() != null ? l.getFrozenUntill().toString() : null); + result.put("frozenUntill", l.getFrozenUntil() != null ? l.getFrozenUntil().toString() : null); result.put("currentTask", l.getCurrentTask() != null ? l.getCurrentTask() : null); result.put("currentTaskDescription", l.getCurrentTaskDescription()); - result.put("taskFrozenUntil", l.getTaskFrozenUntil() != null ? l.getTaskFrozenUntil().toString() : null); + result.put("taskFrozenUntil", l.getTaskUntil() != null ? l.getTaskUntil().toString() : null); result.put("hygieneEnabled", hygieneEnabled); result.put("hygieneOpeningDue", hygieneOpeningDue); result.put("hygieneSecondsRemaining", hygieneSecondsRemaining); @@ -489,19 +465,19 @@ public class CardLockController { LocalDateTime todayStart = LocalDate.now().atStartOfDay(); LocalDateTime todayEnd = todayStart.plusDays(1); var completed = verificationRepository - .findByLockIdAndVerificationTimeBetweenAndImageIsNotNull(l.getLockId(), todayStart, todayEnd); + .findByLockIdAndCreatedAtBetweenAndImageIsNotNull(l.getLockId(), todayStart, todayEnd); if (!completed.isEmpty()) { var todayV = completed.get(0); - verificationTodayId = todayV.getVerficationId().toString(); - var votes = verificationVoteRepository.findAllByVerificationId(todayV.getVerficationId()); - verificationUpvotes = votes.stream().filter(VerificationVoteEntity::isUpvote).count(); + verificationTodayId = todayV.getDisplayId().toString(); + var votes = verificationVoteRepository.findAllByVerificationId(todayV.getDisplayId()); + verificationUpvotes = votes.stream().filter(CommunityVerificationVoteEntity::isUpvote).count(); verificationDownvotes = votes.stream().filter(v2 -> !v2.isUpvote()).count(); } else { verificationDue = true; - var pending = verificationRepository.findByLockIdAndVerificationTimeBetweenAndImageIsNull(l.getLockId(), + var pending = verificationRepository.findByLockIdAndCreatedAtBetweenAndImageIsNull(l.getLockId(), todayStart, todayEnd); if (!pending.isEmpty()) { - verificationPendingId = pending.get(0).getVerficationId().toString(); + verificationPendingId = pending.get(0).getDisplayId().toString(); verificationPendingCode = pending.get(0).getCode(); } } @@ -515,20 +491,19 @@ public class CardLockController { result.put("verificationPendingCode", verificationPendingCode); // Abgelaufene Aufgaben prüfen und Strafe anwenden - boolean lockDirty = false; var expiredTasks = assignedTaskRepository.findByLockIdAndStatus(l.getLockId(), "PENDING").stream() .filter(t -> t.getAcceptDeadline().isBefore(LocalDateTime.now())).toList(); - for (var t : expiredTasks) { - t.setStatus("EXPIRED"); - applyAssignedTaskPenalty(l, t); - assignedTaskRepository.save(t); - 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(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE); + if (!expiredTasks.isEmpty()) { + CardLockService penaltyService = cardLockServiceFactory.create(l); + for (var t : expiredTasks) { + t.setStatus("EXPIRED"); + penaltyService.applyAssignedTaskPenalty(t); + assignedTaskRepository.save(t); + 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(), de.oaa.xxx.social.entity.MessageCause.GAME_STATE); + } } - if (lockDirty) - cardlockRepository.save(l); // Ausstehende Keyholder-Aufgaben (ohne Aufgabentext) var pendingAssigned = assignedTaskRepository.findByLockIdAndStatus(l.getLockId(), "PENDING").stream() @@ -546,20 +521,20 @@ public class CardLockController { return m; }).toList(); result.put("assignedTasks", pendingAssigned); - result.put("taskCardMode", l.getTaskCardMode()); + result.put("taskMode", l.getTaskMode()); // Ausstehende Keyholder-Choices - boolean pendingKeyholderChoice = !keyholderTaskChoiceRepository.findByLockIdAndStatus(l.getLockId(), "PENDING") + boolean pendingKeyholderChoice = !keyholderTaskChoiceRepository.findByLockIdAndActiveTrue(l.getLockId()) .isEmpty(); result.put("pendingKeyholderChoice", pendingKeyholderChoice); // Aktive Community-Vote - var activeVotes = communityTaskVoteRepository.findByStatus("ACTIVE").stream() + var activeVotes = communityTaskVoteRepository.findByActiveTrue().stream() .filter(v -> v.getLockId().equals(l.getLockId())).findFirst(); if (activeVotes.isPresent()) { var v = activeVotes.get(); result.put("activeCommunityVote", - Map.of("voteSessionId", v.getVoteSessionId().toString(), "expiresAt", v.getExpiresAt().toString())); + Map.of("voteSessionId", v.getDisplayId().toString(), "expiresAt", v.getExpiresAt().toString())); } // Notfall-Entsperrung: nach 1 Stunde automatisch öffnen @@ -608,30 +583,30 @@ public class CardLockController { LocalDateTime todayEnd = todayStart.plusDays(1); // Existierende Verifikation für heute zurückgeben statt neue anlegen - var existing = verificationRepository.findByLockIdAndVerificationTimeBetweenAndImageIsNull(lockId, todayStart, + var existing = verificationRepository.findByLockIdAndCreatedAtBetweenAndImageIsNull(lockId, todayStart, todayEnd); if (!existing.isEmpty()) { var ev = existing.get(0); - return ResponseEntity.ok(Map.of("verificationId", ev.getVerficationId().toString(), "code", ev.getCode())); + return ResponseEntity.ok(Map.of("verificationId", ev.getDisplayId().toString(), "code", ev.getCode())); } - var completed = verificationRepository.findByLockIdAndVerificationTimeBetweenAndImageIsNotNull(lockId, + var completed = verificationRepository.findByLockIdAndCreatedAtBetweenAndImageIsNotNull(lockId, todayStart, todayEnd); if (!completed.isEmpty()) { var cv = completed.get(0); - return ResponseEntity.ok(Map.of("verificationId", cv.getVerficationId().toString(), "code", cv.getCode())); + return ResponseEntity.ok(Map.of("verificationId", cv.getDisplayId().toString(), "code", cv.getCode())); } - VerificationEntity v = new VerificationEntity(); - v.setVerficationId(UUID.randomUUID()); + CommunityVerificationEntity v = new CommunityVerificationEntity(); + v.setDisplayId(UUID.randomUUID()); v.setLockId(lockId); v.setLockeeId(myId); - v.setCode(CodeCreator.createAlphanumericCode(6)); - v.setVerificationTime(LocalDateTime.now()); + v.setCode(CodeCreator.createAlphanumeric(6)); + v.setCreatedAt(LocalDateTime.now()); if (l.getKeyholder() != null) v.setKeyholderId(l.getKeyholder()); verificationRepository.save(v); - return ResponseEntity.ok(Map.of("verificationId", v.getVerficationId().toString(), "code", v.getCode())); + return ResponseEntity.ok(Map.of("verificationId", v.getDisplayId().toString(), "code", v.getCode())); } @PostMapping(value = "/cardlock/{lockId}/verification/{verificationId}/complete", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @@ -707,10 +682,10 @@ public class CardLockController { LocalDateTime todayStart = LocalDate.now().atStartOfDay(); LocalDateTime todayEnd = todayStart.plusDays(1); - var completed = verificationRepository.findByLockIdAndVerificationTimeBetweenAndImageIsNotNull(lockId, + var completed = verificationRepository.findByLockIdAndCreatedAtBetweenAndImageIsNotNull(lockId, todayStart, todayEnd); for (var v : completed) { - verificationVoteRepository.deleteAllByVerificationId(v.getVerficationId()); + verificationVoteRepository.deleteAllByVerificationId(v.getDisplayId()); verificationRepository.delete(v); } return ResponseEntity.noContent().build(); @@ -866,7 +841,7 @@ public class CardLockController { item.put("lockeeProfilePic", lockee.getProfilePicture()); item.put("totalCards", lock.getAvailableCards() != null ? lock.getAvailableCards().size() : 0); item.put("startTime", lock.getStartTime() != null ? lock.getStartTime().toString() : null); - boolean frozenByKh = lock.getFrozenUntill() != null && lock.getFrozenUntill().isAfter(LocalDateTime.now()) + boolean frozenByKh = lock.getFrozenUntil() != null && lock.getFrozenUntil().isAfter(LocalDateTime.now()) && (lock.getCurrentTask() == null || lock.getCurrentTask().isBlank()); item.put("isFrozenByKeyholder", frozenByKh); result.add(item); @@ -919,16 +894,16 @@ public class CardLockController { if (l.isRequiresVerification()) { LocalDateTime todayStart = LocalDate.now().atStartOfDay(); LocalDateTime todayEnd = todayStart.plusDays(1); - var completed = verificationRepository.findByLockIdAndVerificationTimeBetweenAndImageIsNotNull(lockId, + var completed = verificationRepository.findByLockIdAndCreatedAtBetweenAndImageIsNotNull(lockId, todayStart, todayEnd); if (!completed.isEmpty()) { verificationDoneToday = true; var v = completed.get(0); - var votes = verificationVoteRepository.findAllByVerificationId(v.getVerficationId()); - verificationUpvotes = votes.stream().filter(VerificationVoteEntity::isUpvote).count(); + var votes = verificationVoteRepository.findAllByVerificationId(v.getDisplayId()); + verificationUpvotes = votes.stream().filter(CommunityVerificationVoteEntity::isUpvote).count(); verificationDownvotes = votes.stream().filter(v2 -> !v2.isUpvote()).count(); - verificationTodayId = v.getVerficationId().toString(); - var myVoteOpt = verificationVoteRepository.findByVerificationIdAndUserId(v.getVerficationId(), myId); + verificationTodayId = v.getDisplayId().toString(); + var myVoteOpt = verificationVoteRepository.findByVerificationIdAndUserId(v.getDisplayId(), myId); if (myVoteOpt.isPresent()) { verificationMyVote = myVoteOpt.get().isUpvote() ? "upvote" : "downvote"; } else if (v.getImage() != null) { @@ -954,9 +929,9 @@ public class CardLockController { result.put("cardCounts", cardCounts); result.put("openPicks", l.getOpenPicks() != null ? l.getOpenPicks() : 0); result.put("nextCardIn", l.getNextCardIn() != null ? l.getNextCardIn().toString() : null); - result.put("frozenUntill", l.getFrozenUntill() != null ? l.getFrozenUntill().toString() : null); - result.put("taskFrozenUntil", l.getTaskFrozenUntil() != null ? l.getTaskFrozenUntil().toString() : null); - boolean isFrozenByKeyholder = l.getFrozenUntill() != null && l.getFrozenUntill().isAfter(LocalDateTime.now()); + result.put("frozenUntill", l.getFrozenUntil() != null ? l.getFrozenUntil().toString() : null); + result.put("taskFrozenUntil", l.getTaskUntil() != null ? l.getTaskUntil().toString() : null); + boolean isFrozenByKeyholder = l.getFrozenUntil() != null && l.getFrozenUntil().isAfter(LocalDateTime.now()); result.put("isFrozenByKeyholder", isFrozenByKeyholder); result.put("currentTask", l.getCurrentTask()); result.put("currentTaskDescription", l.getCurrentTaskDescription()); @@ -978,7 +953,7 @@ public class CardLockController { if (l.getTasks() != null) { var taskList = l.getTasks().stream().map(t -> { Map m = new LinkedHashMap<>(); - m.put("title", t.resolveTitle()); + m.put("title", t.getTitle()); m.put("description", t.getDescription() != null ? t.getDescription() : ""); m.put("minutes", t.getMinutes() != null ? t.getMinutes() : 0); return m; @@ -1002,7 +977,7 @@ public class CardLockController { return m; }).toList(); result.put("pendingAssignedTasks", pendingAssigned); - result.put("taskCardMode", l.getTaskCardMode()); + result.put("taskMode", l.getTaskMode()); // Ausstehende Task-Karten-Choices (KEYHOLDER-Modus) List lockTasks = l.getTasks() != null ? l.getTasks() : List.of(); @@ -1011,12 +986,12 @@ public class CardLockController { Task t = lockTasks.get(i); Map tm = new LinkedHashMap<>(); tm.put("index", i); - tm.put("title", t.resolveTitle()); + tm.put("title", t.getTitle()); tm.put("description", t.getDescription() != null ? t.getDescription() : ""); tm.put("minutes", t.getMinutes() != null ? t.getMinutes() : 0); taskListForChoice.add(tm); } - var pendingChoices = keyholderTaskChoiceRepository.findByLockIdAndStatus(lockId, "PENDING").stream().map(c -> { + var pendingChoices = keyholderTaskChoiceRepository.findByLockIdAndActiveTrue(lockId).stream().map(c -> { Map cm = new LinkedHashMap<>(); cm.put("choiceId", c.getChoiceId().toString()); cm.put("createdAt", c.getCreatedAt().toString()); @@ -1053,7 +1028,7 @@ public class CardLockController { service.unlock(l.getUnlockCode()); var verifications = verificationRepository.findByLockId(lockId); - verifications.forEach(v -> verificationVoteRepository.deleteAllByVerificationId(v.getVerficationId())); + verifications.forEach(v -> verificationVoteRepository.deleteAllByVerificationId(v.getDisplayId())); verificationRepository.deleteAll(verifications); invitationRepository.deleteByLockId(lockId); cardlockRepository.deleteById(lockId); @@ -1180,24 +1155,6 @@ public class CardLockController { // ── Hilfsmethoden ────────────────────────────────────────────────────────── - private void applyAssignedTaskPenalty(CardLockEntity l, AssignedTaskEntity task) { - if (task.getPenaltyFreezeMinutes() != null && task.getPenaltyFreezeMinutes() > 0) { - LocalDateTime until = LocalDateTime.now().plusMinutes(task.getPenaltyFreezeMinutes()); - // Bestehenden Freeze nur verlängern, nie verkürzen - if (l.getFrozenUntill() == null || until.isAfter(l.getFrozenUntill())) { - l.setFrozenUntill(until); - l.setNextCardIn(until); - } - } - if (task.getPenaltyRedCards() != null && task.getPenaltyRedCards() > 0) { - List cards = new ArrayList<>(l.getAvailableCards() != null ? l.getAvailableCards() : List.of()); - for (int i = 0; i < task.getPenaltyRedCards(); i++) { - cards.add(CardEnum.RED); - } - l.setAvailableCards(cards); - } - } - 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); @@ -1263,9 +1220,9 @@ public class CardLockController { Task task = tasks.get(req.taskIndex()); AssignedTaskEntity assigned = new AssignedTaskEntity(); assigned.setLockId(lockId); - assigned.setTaskTitle(task.resolveTitle()); + assigned.setTaskTitle(task.getTitle()); assigned.setTaskDescription(task.getDescription()); - assigned.setTaskText(task.resolveTitle()); // Compat + assigned.setTaskText(task.getTitle()); // Compat assigned.setTaskMinutes(task.getMinutes()); assigned.setAssignedAt(LocalDateTime.now()); assigned.setAcceptDeadline(LocalDateTime.now().plusMinutes(req.acceptDeadlineMinutes())); @@ -1309,14 +1266,13 @@ public class CardLockController { if (task.getAcceptDeadline().isBefore(LocalDateTime.now())) { // Bereits abgelaufen – Strafe anwenden task.setStatus("EXPIRED"); - applyAssignedTaskPenalty(l, task); + cardLockServiceFactory.create(l).applyAssignedTaskPenalty(task); assignedTaskRepository.save(task); - cardlockRepository.save(l); return ResponseEntity.status(409) .body(Map.of("error", "Die Annahme-Frist ist abgelaufen. Die Strafe wurde angewendet.")); } boolean hasActiveTask = (l.getCurrentTask() != null && !l.getCurrentTask().isBlank()) - || (l.getTaskFrozenUntil() != null && l.getTaskFrozenUntil().isAfter(LocalDateTime.now())); + || (l.getTaskUntil() != null && l.getTaskUntil().isAfter(LocalDateTime.now())); if (hasActiveTask) return ResponseEntity.status(409).body(Map.of("error", "Du hast bereits eine laufende Aufgabe.")); @@ -1325,7 +1281,7 @@ public class CardLockController { l.setCurrentTask(title); l.setCurrentTaskDescription(task.getTaskDescription()); if (task.getTaskMinutes() != null && task.getTaskMinutes() > 0) { - l.setTaskFrozenUntil(LocalDateTime.now().plusMinutes(task.getTaskMinutes())); + l.setTaskUntil(LocalDateTime.now().plusMinutes(task.getTaskMinutes())); // Fälligkeit aller anderen offenen Aufgaben um die Task-Dauer verschieben final int extraMinutes = task.getTaskMinutes(); @@ -1370,9 +1326,8 @@ public class CardLockController { return ResponseEntity.status(409).body(Map.of("error", "Diese Aufgabe ist nicht mehr ausstehend.")); task.setStatus("DECLINED"); - applyAssignedTaskPenalty(l, task); + cardLockServiceFactory.create(l).applyAssignedTaskPenalty(task); assignedTaskRepository.save(task); - cardlockRepository.save(l); sendMessage(myId, l.getKeyholder(), meOpt.get().getName() + " hat die gestellte Aufgabe abgelehnt. Die Strafe wurde angewendet.", @@ -1443,7 +1398,7 @@ public class CardLockController { return ResponseEntity.badRequest().body(Map.of("error", "Zeitpunkt muss in der Zukunft liegen.")); } - l.setFrozenUntill(until); + l.setFrozenUntil(until); cardlockRepository.save(l); sendMessage(myId, l.getLockee(), @@ -1476,7 +1431,7 @@ public class CardLockController { "Das Lock ist durch eine Aufgabe eingefroren und kann nicht manuell entfroren werden.")); } - l.setFrozenUntill(null); + l.setFrozenUntil(null); cardlockRepository.save(l); sendMessage(myId, l.getLockee(), me.getName() + " hat dein Lock wieder entfroren.", diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockEntity.java index 47597df..646e3ff 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockEntity.java @@ -2,40 +2,24 @@ package de.oaa.xxx.games.chastity.cardlock; import java.time.LocalDateTime; import java.util.List; -import java.util.UUID; +import de.oaa.xxx.games.chastity.common.BaseLockEntity; import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.TaskListConverter; -import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -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 = "card_lock") -public class CardLockEntity { +@DiscriminatorValue("CARDLOCK") +public class CardLockEntity extends BaseLockEntity { - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - private UUID lockId; - @Column - private String name; - - // Settings - @Column(nullable = false) - private UUID lockee; - @Column - private UUID keyholder; @Convert(converter = CardEnumListConverter.class) @Column(columnDefinition = "TEXT") private List initialCards; @@ -47,69 +31,17 @@ public class CardLockEntity { private boolean showRemainingCards; @Column private LocalDateTime latestOpeningtime; - @Column - private LocalDateTime frozenUntill; - @Column - private Integer hygineOpeningDurationMinutes; - @Column - private Integer hygineOpeningEveryMinites; - @Convert(converter = TaskListConverter.class) - @Column(columnDefinition = "TEXT") - private List tasks; - @Column - private boolean requiresVerification; - @Column - private boolean testLock; - @Column - private Integer unlockCodeLength; - + // State @Column - private LocalDateTime startTime; - @Column private LocalDateTime nextCardIn; @Column private Integer openPicks; @Convert(converter = CardEnumListConverter.class) @Column(columnDefinition = "TEXT") private List availableCards; - @Column - private LocalDateTime lastHygineOpening; - @Column - private LocalDateTime tempOpeningTime; // If null, not while hygine opening - @Column - private Integer tempOpeningDuration; - @Column - private TempOpeningReason tempOpeningReason; - @Column - private LocalDateTime unlockTime; - @Column - private String currentTask; - @Column(columnDefinition = "TEXT") - private String currentTaskDescription; - @Column - private LocalDateTime taskFrozenUntil; + @Convert(converter = TaskListConverter.class) @Column(columnDefinition = "TEXT") private List tasksInQueue; - @Column - private String unlockCode; - - /** Keyholder hat Unlock angefordert – nächste Aktion der Lockee zeigt grüne Karte */ - @Column(nullable = false) - private boolean keyholderRequestedUnlock = false; - - /** Lockee hat Notfall-Entsperrung angefordert */ - @Column - private java.time.LocalDateTime emergencyUnlockRequestedAt; - - /** true = System hat automatisch entsperrt (Keyholderin nicht reagiert) */ - @Column(nullable = false) - private boolean emergencyAutoUnlocked = false; - - /** RANDOM | KEYHOLDER | COMMUNITY */ - @Column(nullable = false) - private String taskCardMode = "RANDOM"; - - public String getTaskCardMode() { return taskCardMode != null ? taskCardMode : "RANDOM"; } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java index 243520e..01d73eb 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockService.java @@ -1,332 +1,261 @@ package de.oaa.xxx.games.chastity.cardlock; -import java.time.Duration; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; import java.util.Random; -import java.util.Set; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.oaa.xxx.games.chastity.common.AbstractLockService; +import de.oaa.xxx.games.chastity.common.BaseLockEntity; +import de.oaa.xxx.games.chastity.common.BaseLockService; import de.oaa.xxx.games.chastity.common.CodeCreator; -import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationEntity; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; -import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationRepository; +import de.oaa.xxx.games.chastity.tasks.AssignedTaskEntity; import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; -import de.oaa.xxx.games.chastity.verification.VerificationRepository; -import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; -import de.oaa.xxx.games.history.GameHistoryEntity; import de.oaa.xxx.games.history.GameHistoryRepository; +import de.oaa.xxx.games.history.GameType; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; -public class CardLockService extends AbstractLockService { +public class CardLockService extends BaseLockService { - private static final Logger LOGGER = LoggerFactory.getLogger(CardLockService.class); - private final CardLockEntity lock; - private CardLockRepository cardLockRepository; - private VerificationRepository verificationRepository; - private GameHistoryRepository gameHistoryRepository; - private UserRepository userRepository; - private UnlockCodeHistoryService unlockCodeHistoryService; - private KeyholderNotificationRepository keyholderNotificationRepository; + private static final Logger LOGGER = LoggerFactory.getLogger(CardLockService.class); + private final CardLockEntity lock; + private final CardLockRepository cardLockRepository; + private String pendingTaskMode; - public CardLockService(CardLockEntity lock, VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, CardLockRepository cardLockRepository, - GameHistoryRepository gameHistoryRepository, UserRepository userRepository, KeyholderNotificationRepository keyholderNotificationRepository,UnlockCodeHistoryService unlockCodeHistoryService) { - super(verificationVoteRepository); - this.lock = lock; - this.cardLockRepository = cardLockRepository; - this.verificationRepository = verificationRepository; - this.gameHistoryRepository = gameHistoryRepository; - this.userRepository = userRepository; - this.keyholderNotificationRepository = keyholderNotificationRepository; - this.unlockCodeHistoryService = unlockCodeHistoryService; - } + public CardLockService( + CardLockEntity lock, + CommunityVerificationVoteRepository communityVerificationVoteRepository, + CommunityVerificationRepository communityVerificationRepository, + KeyholderVerificationRepository keyholderVerificationRepository, + GameHistoryRepository gameHistoryRepository, + UserRepository userRepository, + KeyholderNotificationRepository keyholderNotificationRepository, + SystemMessageService systemMessageService, + UnlockCodeHistoryService unlockCodeHistoryService, + KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + CommunityTaskVoteRepository communityTaskVoteRepository, + CardLockRepository cardLockRepository) { + super(communityVerificationVoteRepository, communityVerificationRepository, keyholderVerificationRepository, + gameHistoryRepository, userRepository, keyholderNotificationRepository, systemMessageService, + unlockCodeHistoryService, keyholderTaskChoiceRepository, communityTaskVoteRepository); + this.lock = lock; + this.cardLockRepository = cardLockRepository; + } - public CardDTO getNextCard() { - LOGGER.debug("New Card requested by user {}", lock.getLockee()); - CardDTO card = null; - if (lock.isKeyholderRequestedUnlock() || (lock.getLatestOpeningtime() != null && lock.getLatestOpeningtime().isAfter(LocalDateTime.now()))) { - card = getGreenCard(); - } else if (lock.isAccumulatePicks()) { - if (lock.getNextCardIn().isBefore(LocalDateTime.now())) { - lock.setOpenPicks(lock.getOpenPicks() == null ? 1 : lock.getOpenPicks() + 1); - } - if (lock.getOpenPicks() != null && lock.getOpenPicks() > 0) { - lock.setOpenPicks(lock.getOpenPicks() - 1); - card = getRandomCard(); - } - } else { - if (lock.getNextCardIn().isBefore(LocalDateTime.now())) { - lock.setNextCardIn(LocalDateTime.now().plusMinutes(lock.getPickEveryMinute())); - card = getRandomCard(); - } - } - cardLockRepository.save(lock); - return card; - } + // ── Abstract method implementations ────────────────────────────────────── - private CardDTO getRandomCard() { - var cards = lock.getAvailableCards(); - if (!cards.isEmpty()) { - var card = cards.get(new Random().nextInt(cards.size())); - LOGGER.debug("Card drafted: {}", card); - lock.getAvailableCards().remove(card); - return card.get().processCard(this); - } - LOGGER.error("Keine Karten mehr im Lock - generiere Notfall Grüne Karte"); - return getGreenCard(); - } + @Override + protected BaseLockEntity getLock() { + return lock; + } - private CardDTO getGreenCard() { - return new CardDTO(CardEnum.GREEN, lock.getUnlockCode()); - } + @Override + protected void saveLock() { + cardLockRepository.save(lock); + } - public String doubleUp() { - var cards = lock.getAvailableCards(); - LOGGER.debug("Double up {} cards", cards.size()); - lock.getAvailableCards().addAll(cards); - LOGGER.debug("Now {} cards", lock.getAvailableCards().size()); - return ""; - } + @Override + protected GameType getGameType() { + return GameType.CARDLOCK; + } - public String reset() { - LOGGER.debug("Reset to initial cards"); - lock.setAvailableCards(lock.getInitialCards()); - return ""; - } - - public String green() { - LOGGER.debug("Green Card drafted"); - return lock.getUnlockCode(); - } + @Override + protected void applyHygieneOvertime(Long overtime) { + if (lock.getFrozenUntil() != null) { + lock.setFrozenUntil(lock.getFrozenUntil().plusMinutes(overtime * 4)); + } else { + lock.setFrozenUntil(LocalDateTime.now().plusMinutes(overtime * 4)); + } + } - public void unlock(String unlockCode) { - this.lock.setUnlockTime(LocalDateTime.now()); - boolean valid = true; - if (lock.isEmergencyAutoUnlocked()) { - valid = false; - LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); - } - if (lock.isTestLock()) { - valid = false; - } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { - Set verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream() - .filter(verification -> isValid(verification)) - .map(verification -> verification.getVerificationTime().toLocalDate()) - .collect(Collectors.toSet()); - - LocalDate current = this.lock.getStartTime().toLocalDate(); - LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1); - - while (!current.isAfter(last)) { - if (!verifications.contains(current)) { - valid = false; - LOGGER.debug("Lock invalid - no daily verification on %s", current.toString()); - break; - } - current = current.plusDays(1); - } - } + // ── Card drawing ────────────────────────────────────────────────────────── - lock.setUnlockTime(LocalDateTime.now()); - LOGGER.debug("Unlocked at {}", lock.getUnlockTime()); - cardLockRepository.save(lock); + public CardDTO getNextCard() { + LOGGER.debug("New Card requested by user {}", lock.getLockee()); + CardDTO card = null; + if (lock.isKeyholderRequestedUnlock() + || (lock.getLatestOpeningtime() != null && lock.getLatestOpeningtime().isAfter(LocalDateTime.now()))) { + card = getGreenCard(); + } else if (lock.isAccumulatePicks()) { + if (lock.getNextCardIn().isBefore(LocalDateTime.now())) { + lock.setOpenPicks(lock.getOpenPicks() == null ? 1 : lock.getOpenPicks() + 1); + } + if (lock.getOpenPicks() != null && lock.getOpenPicks() > 0) { + lock.setOpenPicks(lock.getOpenPicks() - 1); + card = getRandomCard(); + } + } else { + if (lock.getNextCardIn().isBefore(LocalDateTime.now())) { + lock.setNextCardIn(LocalDateTime.now().plusMinutes(lock.getPickEveryMinute())); + card = getRandomCard(); + } + } + cardLockRepository.save(lock); + return card; + } - if (valid) { - long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes(); + private CardDTO getRandomCard() { + var cards = lock.getAvailableCards(); + if (!cards.isEmpty()) { + var card = cards.get(new Random().nextInt(cards.size())); + LOGGER.debug("Card drafted: {}", card); + lock.getAvailableCards().remove(card); + return card.get().processCard(this); + } + LOGGER.error("Keine Karten mehr im Lock - generiere Notfall Grüne Karte"); + return getGreenCard(); + } - // 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) { - entry.addParticipant(lock.getKeyholder(), de.oaa.xxx.games.history.GameRole.KEYHOLDER); - } - gameHistoryRepository.save(entry); + private CardDTO getGreenCard() { + return new CardDTO(CardEnum.GREEN, lock.getUnlockCode()); + } - int minutes = (int) durationMinutes; - userRepository.findById(lock.getLockee()).ifPresent(u -> { - u.setLockeeXp(u.getLockeeXp() + minutes); - userRepository.save(u); - }); - if (lock.getKeyholder() != null) { - userRepository.findById(lock.getKeyholder()).ifPresent(u -> { - u.setKeyholderXp(u.getKeyholderXp() + minutes); - userRepository.save(u); - }); - } - } - } - - public void putBackGreen() { - LOGGER.debug("Green Card was put Back"); - lock.getAvailableCards().add(CardEnum.GREEN); - cardLockRepository.save(lock); - } - - public String freeze() { - var multiplier = lock.getPickEveryMinute() * new Random().nextDouble(1.0, 4.0); - freeze(multiplier); - return ""; - } + // ── Card effects ────────────────────────────────────────────────────────── - private String freeze(double multiplier) { - LocalDateTime frozenTill = LocalDateTime.now().plus((long) multiplier, ChronoUnit.MINUTES); - lock.setFrozenUntill(frozenTill); - lock.setNextCardIn(frozenTill); - LOGGER.debug("Frozen until {}", lock.getFrozenUntill()); - return ""; - } + public String doubleUp() { + var cards = lock.getAvailableCards(); + LOGGER.debug("Double up {} cards", cards.size()); + lock.getAvailableCards().addAll(cards); + LOGGER.debug("Now {} cards", lock.getAvailableCards().size()); + return ""; + } - public String task() { - // Non-RANDOM modes are handled by the controller after the card is drawn - if (!"RANDOM".equals(lock.getTaskCardMode())) { - LOGGER.debug("Task card drawn in {} mode – skipping random assignment", lock.getTaskCardMode()); - return ""; - } - LOGGER.debug("Apply random task"); - var tasks = lock.getTasks(); - if (!tasks.isEmpty()) { - task(tasks.get(new Random().nextInt(tasks.size()))); - } - return ""; - } + public String reset() { + LOGGER.debug("Reset to initial cards"); + lock.setAvailableCards(lock.getInitialCards()); + return ""; + } - public String task(Task task) { - LOGGER.debug("Apply task {}", task); - lock.setCurrentTask(task.resolveTitle()); - lock.setCurrentTaskDescription(task.getDescription()); - if (task.getMinutes() != null && task.getMinutes() > 0) { - lock.setTaskFrozenUntil(LocalDateTime.now().plusMinutes(task.getMinutes())); - } - return ""; - } + public String green() { + LOGGER.debug("Green Card drafted"); + return lock.getUnlockCode(); + } - public String clearTask() { - LOGGER.debug("Clear task"); - lock.setCurrentTask(null); - lock.setCurrentTaskDescription(null); - lock.setTaskFrozenUntil(null); - return ""; - } + public String freeze() { + var multiplier = lock.getPickEveryMinute() * new Random().nextDouble(1.0, 4.0); + freeze(multiplier); + return ""; + } - public String redCard() { - return ""; - } + private String freeze(double multiplier) { + LocalDateTime frozenTill = LocalDateTime.now().plus((long) multiplier, ChronoUnit.MINUTES); + lock.setFrozenUntil(frozenTill); + lock.setNextCardIn(frozenTill); + LOGGER.debug("Frozen until {}", lock.getFrozenUntil()); + return ""; + } - public String yellowCard() { - Random random = new Random(); - if (random.nextBoolean()) { - for (int i = 0; i < random.nextInt(1, 3); i++) { - LOGGER.debug("Adding Red card"); - lock.getAvailableCards().add(CardEnum.RED); - } - } else { - for (int i = 0; i < random.nextInt(1, 3); i++) { - LOGGER.debug("Removing Red card if possible"); - lock.getAvailableCards().remove(CardEnum.RED); - } - } - return ""; - } - - public void startHygieneOpening() { - tempOperning(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes()); - } - - private Long calcOvertime() { - LocalDateTime now = LocalDateTime.now(); - Long overtime = null; - if (lock.getTempOpeningTime() != null && lock.getTempOpeningDuration() != null) { - LocalDateTime dueTime = lock.getTempOpeningTime().plusMinutes(lock.getTempOpeningDuration()); - if (LocalDateTime.now().isAfter(dueTime)) { - overtime = ChronoUnit.MINUTES.between(dueTime, now); - } - } - return overtime; - } - - public String endHygieneOpening() { - LocalDateTime now = LocalDateTime.now(); + /** Called by TaskCard. Dispatches based on TaskMode and stores result for controller. */ + public String task() { + switch (lock.getTaskMode()) { + case RANDOM -> applyRandomTask(); + case KEYHOLDER -> { + if (lock.isTestLock()) applyRandomTask(); + else startKeyholderVote(); + } + case COMMUNITY -> { + if (lock.isTestLock()) applyRandomTask(); + else startCommunityVote(); + } + } + pendingTaskMode = lock.getTaskMode().name(); + return ""; + } - Long overtime = calcOvertime(); - if (overtime != null) { - if (lock.getKeyholder() != null) { - reportKeyholder(overtime); - } - freezeForOvertime(overtime); + /** Returns the TaskMode that was triggered by the last task() call, or null if no task card was drawn. */ + public String getPendingTaskMode() { + return pendingTaskMode; + } - } - lock.setLastHygineOpening(now); - lock.setTempOpeningDuration(null); - lock.setTempOpeningTime(null); - - var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength()); - lock.setUnlockCode(code); - cardLockRepository.save(lock); - return code; - } + public String redCard() { + return ""; + } - private void reportKeyholder(Long overtime) { - KeyholderNotificationEntity notification = new KeyholderNotificationEntity(); - notification.setLockId(lock.getLockId()); - notification.setLockeeId(lock.getLockee()); - notification.setKeyholderUserId(lock.getKeyholder()); - notification.setViolationTime(LocalDateTime.now()); - notification.setOvertimeMinutes(overtime); - keyholderNotificationRepository.save(notification); - } + public String yellowCard() { + Random random = new Random(); + if (random.nextBoolean()) { + for (int i = 0; i < random.nextInt(1, 3); i++) { + LOGGER.debug("Adding Red card"); + lock.getAvailableCards().add(CardEnum.RED); + } + } else { + for (int i = 0; i < random.nextInt(1, 3); i++) { + LOGGER.debug("Removing Red card if possible"); + lock.getAvailableCards().remove(CardEnum.RED); + } + } + return ""; + } - private void freezeForOvertime(Long overtime) { - if (lock.getFrozenUntill() != null) { - lock.setFrozenUntill(lock.getFrozenUntill().plusMinutes(overtime * 4)); - } else { - lock.setFrozenUntill(LocalDateTime.now().plusMinutes(overtime * 4)); - } - } - - private void tempOperning(TempOpeningReason reason, Integer duration) { - assert duration != null; - lock.setTempOpeningReason(reason); - lock.setTempOpeningTime(LocalDateTime.now());; - lock.setTempOpeningDuration(duration); - cardLockRepository.save(lock); - unlockCodeHistoryService.save(lock.getLockee(), lock.getLockId(), lock.getName(), lock.getUnlockCode(), reason.toString()); - } + public void putBackGreen() { + LOGGER.debug("Green Card was put Back"); + lock.getAvailableCards().add(CardEnum.GREEN); + cardLockRepository.save(lock); + } - public String cum(boolean tempUnlock) { - if (tempUnlock) { - tempOperning(TempOpeningReason.CARD, 0); // Je länger man braucht, desto länger wird gefreezed - } - return lock.getUnlockCode(); - } - - public String endCumming() { - Long overtime = calcOvertime(); - if (overtime != null) { - if (lock.getKeyholder() == null) { - freezeForOvertime(overtime); - } else { - reportKeyholder(overtime); - } - } - lock.setTempOpeningDuration(null); - lock.setTempOpeningTime(null); - lock.setTempOpeningReason(null); - - var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength()); - lock.setUnlockCode(code); - cardLockRepository.save(lock); - return code; - } + // ── Hygiene opening ─────────────────────────────────────────────────────── + + public void startHygieneOpening() { + startTempOpening(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes()); + } + + // ── Cum cards ───────────────────────────────────────────────────────────── + + public String cum(boolean tempUnlock) { + if (tempUnlock) { + startTempOpening(TempOpeningReason.CARD, 0); + } + return lock.getUnlockCode(); + } + + public String endCumming() { + Long overtime = calcOvertime(); + if (overtime != null) { + if (lock.getKeyholder() == null) { + applyHygieneOvertime(overtime); + } else { + reportKeyholder(overtime); + } + } + lock.setTempOpeningDuration(null); + lock.setTempOpeningTime(null); + lock.setTempOpeningReason(null); + + var code = CodeCreator.createNumeric(lock.getUnlockCodeLength()); + lock.setUnlockCode(code); + cardLockRepository.save(lock); + return code; + } + + // ── Assigned task penalty ───────────────────────────────────────────────── + + public void applyAssignedTaskPenalty(AssignedTaskEntity task) { + if (task.getPenaltyFreezeMinutes() != null && task.getPenaltyFreezeMinutes() > 0) { + LocalDateTime until = LocalDateTime.now().plusMinutes(task.getPenaltyFreezeMinutes()); + if (lock.getFrozenUntil() == null || until.isAfter(lock.getFrozenUntil())) { + lock.setFrozenUntil(until); + lock.setNextCardIn(until); + } + } + if (task.getPenaltyRedCards() != null && task.getPenaltyRedCards() > 0) { + List cards = new ArrayList<>( + lock.getAvailableCards() != null ? lock.getAvailableCards() : List.of()); + for (int i = 0; i < task.getPenaltyRedCards(); i++) { + cards.add(CardEnum.RED); + } + lock.setAvailableCards(cards); + } + cardLockRepository.save(lock); + } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockServiceFactory.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockServiceFactory.java index 3de5d8a..6c15b66 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockServiceFactory.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardLockServiceFactory.java @@ -1,10 +1,14 @@ package de.oaa.xxx.games.chastity.cardlock; import de.oaa.xxx.games.history.GameHistoryRepository; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationRepository; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; -import de.oaa.xxx.games.chastity.verification.VerificationRepository; -import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; import org.springframework.stereotype.Service; @@ -18,43 +22,50 @@ import org.springframework.stereotype.Service; @Service public class CardLockServiceFactory { - private final VerificationRepository verificationRepository; - private final VerificationVoteRepository verificationVoteRepository; private final CardLockRepository cardLockRepository; + private final CommunityVerificationRepository communityVerificationRepository; + private final CommunityVerificationVoteRepository communityVerificationVoteRepository; private final GameHistoryRepository gameHistoryRepository; - private final UserRepository userRepository; - private KeyholderNotificationRepository keyholderNotificationRepository; + private final UserRepository userRepository; private final UnlockCodeHistoryService unlockCodeHistoryService; + private final KeyholderNotificationRepository keyholderNotificationRepository; + private final KeyholderVerificationRepository keyholderVerificationRepository; + private final SystemMessageService systemMessageService; + private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; + private final CommunityTaskVoteRepository communityTaskVoteRepository; - public CardLockServiceFactory(VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, - CardLockRepository cardLockRepository, - GameHistoryRepository gameHistoryRepository, - UserRepository userRepository, - KeyholderNotificationRepository keyholderNotificationRepository, - UnlockCodeHistoryService unlockCodeHistoryService) { - this.verificationRepository = verificationRepository; - this.verificationVoteRepository = verificationVoteRepository; + public CardLockServiceFactory( + CommunityVerificationRepository communityVerificationRepository, + CommunityVerificationVoteRepository communityVerificationVoteRepository, + CardLockRepository cardLockRepository, + GameHistoryRepository gameHistoryRepository, + UserRepository userRepository, + KeyholderNotificationRepository keyholderNotificationRepository, + KeyholderVerificationRepository keyholderVerificationRepository, + UnlockCodeHistoryService unlockCodeHistoryService, + SystemMessageService systemMessageService, + KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + CommunityTaskVoteRepository communityTaskVoteRepository) { this.cardLockRepository = cardLockRepository; + this.communityVerificationRepository = communityVerificationRepository; + this.communityVerificationVoteRepository = communityVerificationVoteRepository; this.gameHistoryRepository = gameHistoryRepository; this.userRepository = userRepository; - this.keyholderNotificationRepository = keyholderNotificationRepository; + this.keyholderNotificationRepository = keyholderNotificationRepository; this.unlockCodeHistoryService = unlockCodeHistoryService; + this.keyholderVerificationRepository = keyholderVerificationRepository; + this.systemMessageService = systemMessageService; + this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; + this.communityTaskVoteRepository = communityTaskVoteRepository; } /** * Erstellt eine neue CardLockService-Instanz für das gegebene Lock. */ public CardLockService create(CardLockEntity lock) { - return new CardLockService( - lock, - verificationRepository, - verificationVoteRepository, - cardLockRepository, - gameHistoryRepository, - userRepository, - keyholderNotificationRepository, - unlockCodeHistoryService - ); + return new CardLockService(lock, communityVerificationVoteRepository, communityVerificationRepository, + keyholderVerificationRepository, gameHistoryRepository, userRepository, + keyholderNotificationRepository, systemMessageService, unlockCodeHistoryService, + keyholderTaskChoiceRepository, communityTaskVoteRepository, cardLockRepository); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateController.java index 3b30448..cb05c50 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateController.java @@ -1,6 +1,7 @@ package de.oaa.xxx.games.chastity.cardlock; import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.tasks.TaskMode; import de.oaa.xxx.user.UserRepository; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -33,7 +34,7 @@ public class CardlockTemplateController { Integer hygineOpeningEveryMinites, List tasks, boolean requiresVerification, - String taskCardMode + TaskMode taskMode ) {} private Map toDto(CardlockTemplateEntity t) { @@ -129,6 +130,6 @@ public class CardlockTemplateController { t.setHygineOpeningDurationMinutes(req.hygineOpeningDurationMinutes()); t.setTasks(req.tasks() != null ? req.tasks() : List.of()); t.setRequiresVerification(req.requiresVerification()); - t.setTaskCardMode(req.taskCardMode() != null ? req.taskCardMode() : "RANDOM"); + t.setTaskMode(req.taskMode() != null ? req.taskMode() : TaskMode.RANDOM); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateEntity.java index dc4da8c..e825b44 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/CardlockTemplateEntity.java @@ -1,64 +1,34 @@ package de.oaa.xxx.games.chastity.cardlock; -import de.oaa.xxx.games.chastity.tasks.Task; -import de.oaa.xxx.games.chastity.tasks.TaskListConverter; -import jakarta.persistence.*; +import java.util.Map; + +import de.oaa.xxx.games.chastity.common.BaseLockTemplateEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; import lombok.Getter; import lombok.Setter; -import java.util.List; -import java.util.Map; -import java.util.UUID; - @Getter @Setter @Entity -@Table(name = "cardlock_template") -public class CardlockTemplateEntity { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - private UUID templateId; - - @Column(nullable = false) - private UUID owner; - - @Column - private String name; +@DiscriminatorValue("CARDLOCK") +public class CardlockTemplateEntity extends BaseLockTemplateEntity { @Convert(converter = CardCountMapConverter.class) @Column(columnDefinition = "TEXT") private Map cardCountsMin; - @Convert(converter = CardCountMapConverter.class) @Column(columnDefinition = "TEXT") private Map cardCountsMax; - @Column private Integer pickEveryMinute; - @Column private boolean accumulatePicks; - @Column private boolean showRemainingCards; - - @Column - private Integer hygineOpeningDurationMinutes; - - @Column - private Integer hygineOpeningEveryMinites; - - @Convert(converter = TaskListConverter.class) - @Column(columnDefinition = "TEXT") - private List tasks; - @Column private boolean requiresVerification; - @Column(nullable = false) - private String taskCardMode = "RANDOM"; - - public String getTaskCardMode() { return taskCardMode != null ? taskCardMode : "RANDOM"; } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/AbstractLockService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/AbstractLockService.java deleted file mode 100644 index 31d920e..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/AbstractLockService.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.oaa.xxx.games.chastity.common; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.oaa.xxx.games.chastity.verification.VerificationEntity; -import de.oaa.xxx.games.chastity.verification.VerificationVoteEntity; -import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; - -public abstract class AbstractLockService { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockService.class); - private final VerificationVoteRepository verificationVoteRepository; - - public AbstractLockService(VerificationVoteRepository verificationVoteRepository) { - this.verificationVoteRepository = verificationVoteRepository; - } - - protected boolean isValid(VerificationEntity entity) { - LOGGER.trace("isValid"); - int count = 0; - for (VerificationVoteEntity vote : verificationVoteRepository.findAllByVerificationId(entity.getVerficationId())) { - if (vote.isUpvote()) { - count++; - } else { - count--; - } - } - return count >= 0; - } -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockController.java new file mode 100644 index 0000000..eb21a41 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockController.java @@ -0,0 +1,22 @@ +package de.oaa.xxx.games.chastity.common; + +import java.util.List; + +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.RestController; + +import de.oaa.xxx.games.chastity.tasks.Task; + +@RestController +@RequestMapping("games/chastity/lock/") +public class BaseLockController { + + @GetMapping("/{lockId}/tasks") + public ResponseEntity> getTasks() { + return null; + } + + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockEntity.java new file mode 100644 index 0000000..889b788 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockEntity.java @@ -0,0 +1,101 @@ +package de.oaa.xxx.games.chastity.common; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.tasks.TaskListConverter; +import de.oaa.xxx.games.chastity.tasks.TaskMode; +import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorType; +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.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "current_lock") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "lock_type", discriminatorType = DiscriminatorType.STRING) +public class BaseLockEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column + private UUID lockId; + @Column(nullable = false) + private String name; + // --- Gemeinsame Settings --- + @Column(nullable = false) + private UUID lockee; + @Column + private UUID keyholder; + @Column(nullable = false) + private boolean testLock; + @Column + private boolean requiresVerification; + @Column + private Integer unlockCodeLength; + @Column + private String unlockCode; + + // --- Timing & Hygiene --- + @Column + private LocalDateTime startTime; + @Column + private LocalDateTime unlockTime; + @Column + private LocalDateTime lastHygineOpening; + @Column + private Integer hygineOpeningDurationMinutes; + @Column + private Integer hygineOpeningEveryMinites; + @Column + private LocalDateTime tempOpeningTime; // If null, not while hygine opening + @Column + private Integer tempOpeningDuration; + @Column + private TempOpeningReason tempOpeningReason; + @Column + private LocalDateTime frozenUntil; + + // --- Aufgaben-System (Basis) --- + @Convert(converter = TaskListConverter.class) + @Column(columnDefinition = "TEXT") + private List tasks; + @Column + private String currentTask; + @Column(columnDefinition = "TEXT") + private String currentTaskDescription; + @Column + private LocalDateTime taskUntil; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private TaskMode taskMode = TaskMode.RANDOM; + + // --- Notfall- & Keyholder-Status --- + @Column(nullable = false) + private boolean keyholderRequestedUnlock = false; + @Column + private LocalDateTime emergencyUnlockRequestedAt; + @Column(nullable = false) + private boolean emergencyAutoUnlocked = false; + + // Getter & Setter + public TaskMode getTaskMode() { + return taskMode != null ? taskMode : TaskMode.RANDOM; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockRepository.java new file mode 100644 index 0000000..63a3b60 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockRepository.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.common; + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BaseLockRepository extends JpaRepository{ + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockService.java new file mode 100644 index 0000000..dd30ab7 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockService.java @@ -0,0 +1,270 @@ +package de.oaa.xxx.games.chastity.common; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteEntity; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationEntity; +import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceEntity; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationRepository; +import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; +import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; +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.social.SystemMessageService; +import de.oaa.xxx.user.UserRepository; + +public abstract class BaseLockService { + + private static final Logger LOGGER = LoggerFactory.getLogger(BaseLockService.class); + + protected final CommunityVerificationVoteRepository communityVerificationVoteRepository; + protected final CommunityVerificationRepository communityVerificationRepository; + protected final KeyholderVerificationRepository keyholderVerificationRepository; + protected final GameHistoryRepository gameHistoryRepository; + protected final UserRepository userRepository; + protected final KeyholderNotificationRepository keyholderNotificationRepository; + protected final SystemMessageService systemMessageService; + protected final UnlockCodeHistoryService unlockCodeHistoryService; + protected final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; + protected final CommunityTaskVoteRepository communityTaskVoteRepository; + + // ── Abstrakte Methoden ──────────────────────────────────────────────────── + + protected abstract BaseLockEntity getLock(); + protected abstract void saveLock(); + protected abstract GameType getGameType(); + /** Wie wird Überschreitung der Hygiene-Öffnung bestraft? CardLock friert ein, TimeLock verlängert die Zeit. */ + protected abstract void applyHygieneOvertime(Long overtime); + + // ── Hook-Methoden (Standard: No-Op) ─────────────────────────────────────── + + /** TimeLock: lockControl.unlock() vor dem finalen Entsperren aufrufen. */ + protected void beforePhysicalUnlock() {} + /** TimeLock: lockControl.lock() nach dem Schließen der Hygiene-Öffnung aufrufen. */ + protected void afterHygieneClosing() {} + + public BaseLockService( + CommunityVerificationVoteRepository communityVerificationVoteRepository, + CommunityVerificationRepository communityVerificationRepository, + KeyholderVerificationRepository keyholderVerificationRepository, + GameHistoryRepository gameHistoryRepository, + UserRepository userRepository, + KeyholderNotificationRepository keyholderNotificationRepository, + SystemMessageService systemMessageService, + UnlockCodeHistoryService unlockCodeHistoryService, + KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + CommunityTaskVoteRepository communityTaskVoteRepository) { + this.communityVerificationVoteRepository = communityVerificationVoteRepository; + this.communityVerificationRepository = communityVerificationRepository; + this.keyholderVerificationRepository = keyholderVerificationRepository; + this.gameHistoryRepository = gameHistoryRepository; + this.userRepository = userRepository; + this.keyholderNotificationRepository = keyholderNotificationRepository; + this.systemMessageService = systemMessageService; + this.unlockCodeHistoryService = unlockCodeHistoryService; + this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; + this.communityTaskVoteRepository = communityTaskVoteRepository; + } + + // ── Gemeinsame Hilfsmethoden ────────────────────────────────────────────── + + protected Long calcOvertime() { + LocalDateTime now = LocalDateTime.now(); + BaseLockEntity lock = getLock(); + if (lock.getTempOpeningTime() != null && lock.getTempOpeningDuration() != null) { + LocalDateTime dueTime = lock.getTempOpeningTime().plusMinutes(lock.getTempOpeningDuration()); + if (now.isAfter(dueTime)) { + return ChronoUnit.MINUTES.between(dueTime, now); + } + } + return null; + } + + protected void reportKeyholder(Long overtime) { + BaseLockEntity lock = getLock(); + KeyholderNotificationEntity notification = new KeyholderNotificationEntity(); + notification.setLockId(lock.getLockId()); + notification.setLockeeId(lock.getLockee()); + notification.setKeyholderUserId(lock.getKeyholder()); + notification.setViolationTime(LocalDateTime.now()); + notification.setOvertimeMinutes(overtime); + keyholderNotificationRepository.save(notification); + } + + protected void sendMessage(UUID senderId, UUID receiverId, String text, String targetUrl, + de.oaa.xxx.social.entity.MessageCause cause) { + systemMessageService.send(senderId, receiverId, text, targetUrl, cause); + } + + // ── Aufgaben ────────────────────────────────────────────────────────────── + + public void task(Task task) { + BaseLockEntity lock = getLock(); + LOGGER.debug("Apply task {}", task); + lock.setCurrentTask(task.getTitle()); + lock.setCurrentTaskDescription(task.getDescription()); + if (task.getMinutes() != null && task.getMinutes() > 0) { + lock.setTaskUntil(LocalDateTime.now().plusMinutes(task.getMinutes())); + } + } + + public String clearTask() { + BaseLockEntity lock = getLock(); + LOGGER.debug("Clear task"); + lock.setCurrentTask(null); + lock.setCurrentTaskDescription(null); + lock.setTaskUntil(null); + return ""; + } + + protected void applyRandomTask() { + LOGGER.debug("Apply random task"); + var tasks = getLock().getTasks(); + if (tasks != null && !tasks.isEmpty()) { + task(tasks.get(new Random().nextInt(tasks.size()))); + } + } + + protected void startKeyholderVote() { + BaseLockEntity lock = getLock(); + KeyholderTaskChoiceEntity choice = new KeyholderTaskChoiceEntity(); + choice.setLockId(lock.getLockId()); + choice.setCreatedAt(LocalDateTime.now()); + choice.setActive(true); + choice.setExpiresAt(LocalDateTime.now().plusHours(1)); + keyholderTaskChoiceRepository.save(choice); + userRepository.findById(lock.getKeyholder()) + .ifPresent(kh -> sendMessage(lock.getLockee(), kh.getUserId(), + "Deine Lockee hat eine Aufgaben-Karte gezogen – wähle eine Aufgabe aus.", + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE)); + } + + protected void startCommunityVote() { + BaseLockEntity lock = getLock(); + CommunityTaskVoteEntity vote = new CommunityTaskVoteEntity(); + vote.setLockId(lock.getLockId()); + vote.setCreatedAt(LocalDateTime.now()); + vote.setExpiresAt(LocalDateTime.now().plusHours(1)); + vote.setActive(true); + communityTaskVoteRepository.save(vote); + } + + // ── Temporäre Öffnung ───────────────────────────────────────────────────── + + protected void startTempOpening(TempOpeningReason reason, Integer duration) { + BaseLockEntity lock = getLock(); + assert duration != null; + lock.setTempOpeningReason(reason); + lock.setTempOpeningTime(LocalDateTime.now()); + lock.setTempOpeningDuration(duration); + saveLock(); + unlockCodeHistoryService.save(lock.getLockee(), lock.getLockId(), lock.getName(), lock.getUnlockCode(), reason.toString()); + } + + public String endHygieneOpening() { + BaseLockEntity lock = getLock(); + LocalDateTime now = LocalDateTime.now(); + Long overtime = calcOvertime(); + if (overtime != null) { + if (lock.getKeyholder() != null) { + reportKeyholder(overtime); + } + applyHygieneOvertime(overtime); + } + afterHygieneClosing(); + lock.setLastHygineOpening(now); + lock.setTempOpeningDuration(null); + lock.setTempOpeningTime(null); + String code = CodeCreator.createNumeric(lock.getUnlockCodeLength()); + lock.setUnlockCode(code); + saveLock(); + return code; + } + + // ── Entsperren ──────────────────────────────────────────────────────────── + + public void unlock(String unlockCode) { + BaseLockEntity lock = getLock(); + beforePhysicalUnlock(); + lock.setUnlockTime(LocalDateTime.now()); + + boolean valid = true; + if (lock.isEmergencyAutoUnlocked()) { + valid = false; + LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); + } else if (lock.isTestLock()) { + valid = false; + } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { + Set verifications; + if (lock.getKeyholder() != null) { + verifications = keyholderVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(v -> v.isValid()) + .map(v -> v.getVerificationDate()) + .collect(Collectors.toSet()); + } else { + verifications = communityVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(v -> v.isValid()) + .map(v -> v.getVerificationDate()) + .collect(Collectors.toSet()); + } + LocalDate current = lock.getStartTime().toLocalDate(); + LocalDate last = lock.getUnlockTime().toLocalDate().minusDays(1); + while (!current.isAfter(last)) { + if (!verifications.contains(current)) { + valid = false; + LOGGER.debug("Lock invalid - no daily verification on {}", current); + break; + } + current = current.plusDays(1); + } + } + + LOGGER.debug("Unlocked at {}", lock.getUnlockTime()); + saveLock(); + + if (valid) { + long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes(); + GameHistoryEntity entry = new GameHistoryEntity(); + entry.setGameType(getGameType()); + entry.setGameName(lock.getName()); + entry.setStartTime(lock.getStartTime()); + entry.setEndTime(lock.getUnlockTime()); + entry.setDurationMinutes(durationMinutes); + entry.addParticipant(lock.getLockee(), GameRole.LOCKEE); + if (lock.getKeyholder() != null) { + entry.addParticipant(lock.getKeyholder(), GameRole.KEYHOLDER); + } + gameHistoryRepository.save(entry); + + int minutes = (int) durationMinutes; + userRepository.findById(lock.getLockee()).ifPresent(u -> { + u.setLockeeXp(u.getLockeeXp() + minutes); + userRepository.save(u); + }); + if (lock.getKeyholder() != null) { + userRepository.findById(lock.getKeyholder()).ifPresent(u -> { + u.setKeyholderXp(u.getKeyholderXp() + minutes); + userRepository.save(u); + }); + } + } + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateController.java new file mode 100644 index 0000000..b509cec --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateController.java @@ -0,0 +1,118 @@ +package de.oaa.xxx.games.chastity.common; + +import de.oaa.xxx.games.chastity.cardlock.CardlockTemplateEntity; +import de.oaa.xxx.games.chastity.timelock.TimeLockTemplateEntity; +import de.oaa.xxx.user.UserRepository; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.*; + +@RestController +@RequestMapping("/templates") +public class BaseLockTemplateController { + + private final BaseLockTemplateRepository templateRepository; + private final UserRepository userRepository; + + public BaseLockTemplateController(BaseLockTemplateRepository templateRepository, + UserRepository userRepository) { + this.templateRepository = templateRepository; + this.userRepository = userRepository; + } + + @GetMapping + public ResponseEntity> list( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int size, + Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var pageable = PageRequest.of(page, Math.min(size, 50), Sort.by("name")); + var pageResult = templateRepository.findByOwner(myId, pageable); + + var content = pageResult.getContent().stream().map(t -> { + Map dto = new LinkedHashMap<>(); + dto.put("templateId", t.getTemplateId()); + dto.put("name", t.getName()); + dto.put("lockType", t instanceof CardlockTemplateEntity ? "CARDLOCK" : "TIMELOCK"); + dto.put("hygineOpeningEveryMinites", t.getHygineOpeningEveryMinites()); + dto.put("hygineOpeningDurationMinutes", t.getHygineOpeningDurationMinutes()); + dto.put("taskCount", t.getTasks() != null ? t.getTasks().size() : 0); + dto.put("requiresVerification", t.isRequiresVerification()); + return dto; + }).toList(); + + Map response = new LinkedHashMap<>(); + response.put("content", content); + response.put("page", pageResult.getNumber()); + response.put("totalPages", pageResult.getTotalPages()); + response.put("last", pageResult.isLast()); + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}") + public ResponseEntity> getById(@PathVariable UUID id, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var opt = templateRepository.findById(id); + if (opt.isEmpty()) return ResponseEntity.notFound().build(); + var t = opt.get(); + if (!t.getOwner().equals(myId)) return ResponseEntity.status(403).build(); + + if (t instanceof CardlockTemplateEntity c) { + return ResponseEntity.ok(toCardlockDto(c)); + } else if (t instanceof TimeLockTemplateEntity tl) { + return ResponseEntity.ok(toTimelockDto(tl)); + } + return ResponseEntity.notFound().build(); + } + + private Map toCardlockDto(CardlockTemplateEntity t) { + Map dto = new LinkedHashMap<>(); + dto.put("_type", "CARDLOCK"); + dto.put("templateId", t.getTemplateId()); + dto.put("name", t.getName()); + dto.put("cardCountsMin", t.getCardCountsMin() != null ? t.getCardCountsMin() : Map.of()); + dto.put("cardCountsMax", t.getCardCountsMax() != null ? t.getCardCountsMax() : Map.of()); + dto.put("pickEveryMinute", t.getPickEveryMinute()); + dto.put("accumulatePicks", t.isAccumulatePicks()); + dto.put("showRemainingCards", t.isShowRemainingCards()); + dto.put("hygineOpeningEveryMinites", t.getHygineOpeningEveryMinites()); + dto.put("hygineOpeningDurationMinutes", t.getHygineOpeningDurationMinutes()); + dto.put("tasks", t.getTasks() != null ? t.getTasks() : List.of()); + dto.put("requiresVerification", t.isRequiresVerification()); + dto.put("taskCardMode", t.getTaskCardMode()); + return dto; + } + + private Map toTimelockDto(TimeLockTemplateEntity t) { + Map dto = new LinkedHashMap<>(); + dto.put("_type", "TIMELOCK"); + dto.put("templateId", t.getTemplateId()); + dto.put("name", t.getName()); + dto.put("minTimeInMinutes", t.getMinTimeInMinutes()); + dto.put("maxTimeInMinutes", t.getMaxTimeInMinutes()); + dto.put("endTimeVisible", t.isEndTimeVisible()); + dto.put("hygineOpeningEveryMinites", t.getHygineOpeningEveryMinites()); + dto.put("hygineOpeningDurationMinutes", t.getHygineOpeningDurationMinutes()); + dto.put("tasks", t.getTasks() != null ? t.getTasks() : List.of()); + dto.put("taskEveryMinutes", t.getTaskEveryMinutes()); + dto.put("minTasksPerDay", t.getMinTasksPerDay()); + dto.put("spinningWheelEntries", t.getSpinningWheelEntries() != null ? t.getSpinningWheelEntries() : List.of()); + dto.put("spinsEveryMinutes", t.getSpinsEveryMinutes()); + dto.put("minSpinsPerDay", t.getMinSpinsPerDay()); + dto.put("requiresVerification", t.isRequiresVerification()); + dto.put("taskMode", t.getTaskCardMode()); + dto.put("penaltyType", t.getPenaltyType()); + dto.put("penaltyValue", t.getPenaltyValue()); + return dto; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateEntity.java new file mode 100644 index 0000000..71056eb --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateEntity.java @@ -0,0 +1,54 @@ +package de.oaa.xxx.games.chastity.common; + +import java.util.List; +import java.util.UUID; + +import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.tasks.TaskListConverter; +import de.oaa.xxx.games.chastity.tasks.TaskMode; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "lock_template") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "lock_type", discriminatorType = DiscriminatorType.STRING) +public class BaseLockTemplateEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column + private UUID templateId; + @Column(nullable = false) + private UUID owner; + @Column + private String name; + @Column + private Integer hygineOpeningDurationMinutes; + @Column + private Integer hygineOpeningEveryMinites; + @Convert(converter = TaskListConverter.class) + @Column(columnDefinition = "TEXT") + private List tasks; + @Column + private boolean requiresVerification; + @Column(nullable = false) + private TaskMode taskMode = TaskMode.RANDOM; + + public TaskMode getTaskCardMode() { + return taskMode != null ? taskMode : TaskMode.RANDOM; + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateRepository.java new file mode 100644 index 0000000..b99b23a --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/BaseLockTemplateRepository.java @@ -0,0 +1,13 @@ +package de.oaa.xxx.games.chastity.common; + +import java.util.List; +import java.util.UUID; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BaseLockTemplateRepository extends JpaRepository{ + List findByOwner(UUID owner); + Page findByOwner(UUID owner, Pageable pageable); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/CodeCreator.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/CodeCreator.java index b029a67..c24c7df 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/CodeCreator.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/CodeCreator.java @@ -11,7 +11,7 @@ public class CodeCreator { return create(digits, CHARS_N); } - public static String createAlphanumericCode(int digits) { + public static String createAlphanumeric(int digits) { return create(digits, CHARS_AN); } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/Verification.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/Verification.java new file mode 100644 index 0000000..038c6d6 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/Verification.java @@ -0,0 +1,38 @@ +package de.oaa.xxx.games.chastity.common; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +public interface Verification { + + LocalDate getVerificationDate(); + + boolean isValid(); + + VerificationCommonDTO toCommonVerification(); + + String getCode(); + + void setCode(String code); + + LocalDateTime getCreatedAt(); + + void setCreatedAt(LocalDateTime createdAt); + + UUID getKeyholderId(); + + void setKeyholderId(UUID id); + + UUID getLockeeId(); + + void setLockeeId(UUID id); + + UUID getLockId(); + + void setLockId(UUID id); + + UUID getId(); + + void setId(UUID id); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/VerificationCommonDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/VerificationCommonDTO.java new file mode 100644 index 0000000..dd8aea4 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/common/VerificationCommonDTO.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.common; + +import java.time.LocalDateTime; +import java.util.UUID; + +public record VerificationCommonDTO(UUID verficationId, UUID lockId, String code, + LocalDateTime createdAt, byte[] image, UUID lockeeId, UUID keyholderID) { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayController.java new file mode 100644 index 0000000..ff6ff7d --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayController.java @@ -0,0 +1,52 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort.Direction; +import org.springframework.data.web.PageableDefault; +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.RestController; + +import de.oaa.xxx.user.UserEntity; +import de.oaa.xxx.user.UserRepository; + +@RestController +@RequestMapping("/games/chastity/community") +public class BaseCommunityDisplayController { + + private final BaseCommunityDisplayRepository baseCommunityDisplayRepository; + private final UserRepository userRepository; + + public BaseCommunityDisplayController(BaseCommunityDisplayRepository baseCommunityDisplayRepository, + UserRepository userRepository) { + this.baseCommunityDisplayRepository = baseCommunityDisplayRepository; + this.userRepository = userRepository; + } + + @GetMapping + public ResponseEntity> getAllDisplays( + @PageableDefault(size = 10, sort = "createdAt", direction = Direction.DESC) Pageable pageable) { + Page page = baseCommunityDisplayRepository.findAll(pageable); + + Set lockeeIds = page.getContent().stream() + .map(BaseCommunityDisplayEntity::getLockeeId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Map nameMap = userRepository.findAllById(lockeeIds).stream() + .collect(Collectors.toMap(UserEntity::getUserId, UserEntity::getName)); + + Page result = page.map(e -> + e.toBaseCommunityDisplay(nameMap.getOrDefault(e.getLockeeId(), ""))); + + return ResponseEntity.ok(result); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayDTO.java new file mode 100644 index 0000000..4f9edd2 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayDTO.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.UUID; + +public record BaseCommunityDisplayDTO(UUID displayId, UUID lockId, LocalDateTime createdAt, UUID lockeeId, + UUID keyholderId, String type, String lockeeName) { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayEntity.java new file mode 100644 index 0000000..1db476e --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayEntity.java @@ -0,0 +1,45 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.UUID; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "community_display") +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "display_type", discriminatorType = DiscriminatorType.STRING) +public abstract class BaseCommunityDisplayEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column + private UUID displayId; + @Column(nullable = false) + private UUID lockId; + @Column(nullable = false) + private LocalDateTime createdAt; + @Column(nullable = false) + private UUID lockeeId; + @Column + private UUID keyholderId; + + public abstract String getType(); + + public BaseCommunityDisplayDTO toBaseCommunityDisplay(String lockeeName) { + return new BaseCommunityDisplayDTO(displayId, lockId, createdAt, lockeeId, keyholderId, getType(), lockeeName); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayRepository.java new file mode 100644 index 0000000..7bbe1a1 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/BaseCommunityDisplayRepository.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BaseCommunityDisplayRepository extends JpaRepository { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryController.java new file mode 100644 index 0000000..cad9de4 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryController.java @@ -0,0 +1,30 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/games/chastity/community/pillory") +public class CommunityPilloryController { + + private CommunityPilloryRepository repo; + + public CommunityPilloryController(CommunityPilloryRepository repo) { + this.repo = repo; + + } + + @GetMapping("/{id}") + public ResponseEntity getPillory(@PathVariable UUID id) { + var pillory = repo.findById(id); + if (pillory.isPresent()) { + return ResponseEntity.ok(pillory.get().toPillory()); + } + return ResponseEntity.notFound().build(); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryDTO.java new file mode 100644 index 0000000..a31ea7a --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryDTO.java @@ -0,0 +1,10 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.UUID; + +public record CommunityPilloryDTO(UUID displayId, UUID lockId, LocalDateTime createdAt, UUID lockeeId, + UUID keyholderId, CommunityPilloryReason reason, String message) { +} + + diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryEntity.java new file mode 100644 index 0000000..a199e2c --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryEntity.java @@ -0,0 +1,27 @@ +package de.oaa.xxx.games.chastity.community; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@DiscriminatorValue("PILLORY") +public class CommunityPilloryEntity extends BaseCommunityDisplayEntity { + + @Column + private CommunityPilloryReason reason; + @Column + private String message; + + @Override + public String getType() { return "PILLORY"; } + + public CommunityPilloryDTO toPillory() { + return new CommunityPilloryDTO(getDisplayId(), getLockId(), getCreatedAt(), getLockeeId(), getKeyholderId(), + reason, message); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryReason.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryReason.java new file mode 100644 index 0000000..a764c69 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryReason.java @@ -0,0 +1,7 @@ +package de.oaa.xxx.games.chastity.community; + +public enum CommunityPilloryReason { + + HYGIENE_OPENING_EXEEDED, + KEYHOLDER_DESCESSION; +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryRepository.java new file mode 100644 index 0000000..646670d --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityPilloryRepository.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommunityPilloryRepository extends JpaRepository { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteController.java new file mode 100644 index 0000000..c6cff10 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteController.java @@ -0,0 +1,111 @@ +package de.oaa.xxx.games.chastity.community; + +import java.security.Principal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.UUID; + +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.oaa.xxx.games.chastity.common.BaseLockRepository; +import de.oaa.xxx.user.UserRepository; + +@RestController +@RequestMapping("/games/chastity/community/taskvote") +public class CommunityTaskVoteController { + + private final UserRepository userRepository; + private final CommunityTaskVoteRepository taskVoteRepository; + private final CommunityTaskVoteEntryRepository taskVoteEntryRepository; + private final BaseLockRepository baseLockRepository; + + public CommunityTaskVoteController(UserRepository userRepository, + CommunityTaskVoteRepository taskVoteRepository, + CommunityTaskVoteEntryRepository taskVoteEntryRepository, + BaseLockRepository baseLockRepository) { + this.userRepository = userRepository; + this.taskVoteRepository = taskVoteRepository; + this.taskVoteEntryRepository = taskVoteEntryRepository; + this.baseLockRepository = baseLockRepository; + } + + @GetMapping("/{displayId}") + public ResponseEntity getVote(@PathVariable UUID displayId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) + return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var voteOpt = taskVoteRepository.findById(displayId); + if (voteOpt.isEmpty() || !voteOpt.get().isActive()) { + return ResponseEntity.noContent().build(); + } + var vote = voteOpt.get(); + var lockOpt = baseLockRepository.findById(vote.getLockId()); + if (lockOpt.isEmpty()) { + return ResponseEntity.noContent().build(); + } + var lock = lockOpt.get(); + boolean isOwnLock = lock.getLockee().equals(myId); + var entryList = new ArrayList(); + var tasks = lock.getTasks(); + var userVote = taskVoteEntryRepository.findByDisplayIdAndUserId(displayId, myId); + for (int i = 0; i < tasks.size(); i++) { + var task = tasks.get(i); + boolean myVote = false; + int votes = taskVoteEntryRepository.countByDisplayIdAndTaskIndex(displayId, i); + if (userVote != null && userVote.getTaskIndex() == i) { + myVote = true; + } + entryList.add(new CommunityTaskVoteDisplayEntryDTO(task.getTitle(), + task.getDescription(), task.getMinutes(), votes, myVote)); + } + return ResponseEntity.ok(new CommunityTaskVoteDisplayDTO(displayId, voteOpt.get().getCreatedAt(), displayId, myId, isOwnLock, entryList)); + } + + + @PostMapping("/{displayId}/vote/{taskIndex}") + @Transactional + public ResponseEntity castVote(@PathVariable UUID displayId, @PathVariable int taskIndex, + Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) + return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var voteOpt = taskVoteRepository.findById(displayId); + if (voteOpt.isEmpty()) + return ResponseEntity.notFound().build(); + var vote = voteOpt.get(); + if (!vote.isActive() || vote.getExpiresAt().isBefore(LocalDateTime.now())) + return ResponseEntity.status(409).build(); + + var lockOpt = baseLockRepository.findById(vote.getLockId()); + if (lockOpt.isEmpty()) + return ResponseEntity.notFound().build(); + var lock = lockOpt.get(); + + if (lock.getLockee().equals(myId)) + return ResponseEntity.status(403).build(); + + if (lock.getTasks() == null || taskIndex < 0 || taskIndex >= lock.getTasks().size()) + return ResponseEntity.badRequest().build(); + + if (taskVoteEntryRepository.existsByDisplayIdAndUserId(displayId, myId)) + return ResponseEntity.status(409).build(); + + CommunityTaskVoteEntryEntity entry = new CommunityTaskVoteEntryEntity(); + entry.setDisplayId(displayId); + entry.setUserId(myId); + entry.setTaskIndex(taskIndex); + taskVoteEntryRepository.save(entry); + + return ResponseEntity.noContent().build(); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDTO.java new file mode 100644 index 0000000..f8df8a4 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDTO.java @@ -0,0 +1,10 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public record CommunityTaskVoteDTO(UUID displayId, UUID lockId, LocalDateTime createdAt, UUID lockeeId, + UUID keyholderId, boolean active, LocalDateTime expiresAt, int winningTaskIndex, List entries) { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayDTO.java new file mode 100644 index 0000000..7ba5a3d --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayDTO.java @@ -0,0 +1,9 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public record CommunityTaskVoteDisplayDTO(UUID displayId, LocalDateTime createdAt, UUID lockeeId, UUID keyholderId, + boolean isOwnLock, List entries) { +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayEntryDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayEntryDTO.java new file mode 100644 index 0000000..75ec2c5 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteDisplayEntryDTO.java @@ -0,0 +1,5 @@ +package de.oaa.xxx.games.chastity.community; + +public record CommunityTaskVoteDisplayEntryDTO(String title, String description, Integer minutes, Integer votes, + boolean ownVote) { +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntity.java new file mode 100644 index 0000000..f05012c --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntity.java @@ -0,0 +1,33 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.List; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@DiscriminatorValue("TASK_VOTE") +public class CommunityTaskVoteEntity extends BaseCommunityDisplayEntity { + + @Column + private boolean active; + @Column(nullable = false) + private LocalDateTime expiresAt; + /** null until completed */ + @Column + private Integer winningTaskIndex; + + @Override + public String getType() { return "TASK_VOTE"; } + + public CommunityTaskVoteDTO toCommunityTaskVote(List entries) { + return new CommunityTaskVoteDTO(getDisplayId(), getLockId(), expiresAt, getLockeeId(), getKeyholderId(), active, + expiresAt, 0, entries); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryDTO.java new file mode 100644 index 0000000..131df30 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryDTO.java @@ -0,0 +1,7 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +public record CommunityTaskVoteEntryDTO(UUID entryId, UUID displayId, UUID userId, int taskIndex) { + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryEntity.java similarity index 64% rename from xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryEntity.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryEntity.java index 86e5515..0fdfbad 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryEntity.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.games.chastity.vote; +package de.oaa.xxx.games.chastity.community; import jakarta.persistence.*; import lombok.Getter; @@ -18,11 +18,15 @@ public class CommunityTaskVoteEntryEntity { private UUID entryId; @Column(nullable = false) - private UUID voteSessionId; + private UUID displayId; @Column(nullable = false) - private UUID voterUserId; + private UUID userId; @Column(nullable = false) private int taskIndex; + + public CommunityTaskVoteEntryDTO toCommunityTaskVoteEntry() { + return new CommunityTaskVoteEntryDTO(entryId, displayId, userId, taskIndex); + } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryRepository.java new file mode 100644 index 0000000..fa60bce --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteEntryRepository.java @@ -0,0 +1,12 @@ +package de.oaa.xxx.games.chastity.community; + +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; +import java.util.UUID; + +public interface CommunityTaskVoteEntryRepository extends JpaRepository { + List findByDisplayId(UUID voteSessionId); + boolean existsByDisplayIdAndUserId(UUID displayId, UUID userId); + CommunityTaskVoteEntryEntity findByDisplayIdAndUserId(UUID displayId, UUID userId); + Integer countByDisplayIdAndTaskIndex(UUID displayId, int taskIndex); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteRepository.java new file mode 100644 index 0000000..a9487be --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteRepository.java @@ -0,0 +1,12 @@ +package de.oaa.xxx.games.chastity.community; + +import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public interface CommunityTaskVoteRepository extends JpaRepository { + List findByActiveTrue(); + List findByActiveTrueAndExpiresAtBefore(LocalDateTime time); + boolean existsByLockIdAndActiveTrue(UUID lockId); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteScheduler.java similarity index 78% rename from xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteScheduler.java index 18a75c1..d43ada6 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskVoteScheduler.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityTaskVoteScheduler.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.games.chastity.cardlock; +package de.oaa.xxx.games.chastity.community; import java.time.LocalDateTime; import java.util.ArrayList; @@ -13,25 +13,24 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import de.oaa.xxx.games.chastity.cardlock.CardlockRepository; 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.games.chastity.vote.CommunityTaskVoteEntryRepository; -import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository; import de.oaa.xxx.social.SystemMessageService; @Component -public class TaskVoteScheduler { +public class CommunityTaskVoteScheduler { - private static final Logger LOG = LoggerFactory.getLogger(TaskVoteScheduler.class); + private static final Logger LOG = LoggerFactory.getLogger(CommunityTaskVoteScheduler.class); private final CommunityTaskVoteRepository communityTaskVoteRepository; private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository; private final CardlockRepository cardlockRepository; private final AssignedTaskRepository assignedTaskRepository; - private final SystemMessageService systemMessageService; + private SystemMessageService systemMessageService; - public TaskVoteScheduler(CommunityTaskVoteRepository communityTaskVoteRepository, + public CommunityTaskVoteScheduler(CommunityTaskVoteRepository communityTaskVoteRepository, CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository, CardlockRepository cardlockRepository, AssignedTaskRepository assignedTaskRepository, @@ -47,27 +46,26 @@ public class TaskVoteScheduler { @Transactional public void processExpiredVotes() { var expired = communityTaskVoteRepository - .findByStatusAndExpiresAtBefore("ACTIVE", LocalDateTime.now()); + .findByActiveTrueAndExpiresAtBefore(LocalDateTime.now()); for (var vote : expired) { - LOG.debug("Processing expired community task vote {}", vote.getVoteSessionId()); + LOG.debug("Processing expired community task vote {}", vote.getDisplayId()); var lockOpt = cardlockRepository.findById(vote.getLockId()); if (lockOpt.isEmpty()) { - vote.setStatus("COMPLETED"); + vote.setActive(false); communityTaskVoteRepository.save(vote); continue; } var lock = lockOpt.get(); List tasks = lock.getTasks(); if (tasks == null || tasks.isEmpty()) { - vote.setStatus("COMPLETED"); + vote.setActive(false); communityTaskVoteRepository.save(vote); continue; } - // Stimmen auszählen - var entries = communityTaskVoteEntryRepository.findByVoteSessionId(vote.getVoteSessionId()); + var entries = communityTaskVoteEntryRepository.findByDisplayId(vote.getDisplayId()); int winnerIndex; if (entries.isEmpty()) { winnerIndex = new Random().nextInt(tasks.size()); @@ -80,7 +78,6 @@ public class TaskVoteScheduler { } } int max = Arrays.stream(counts).max().getAsInt(); - // Alle Tasks mit Maximalstimmen sammeln → zufällige Auswahl bei Gleichstand List winners = new ArrayList<>(); for (int i = 0; i < counts.length; i++) { if (counts[i] == max) winners.add(i); @@ -92,22 +89,21 @@ public class TaskVoteScheduler { Task task = tasks.get(winnerIndex); AssignedTaskEntity assigned = new AssignedTaskEntity(); assigned.setLockId(lock.getLockId()); - assigned.setTaskTitle(task.resolveTitle()); + assigned.setTaskTitle(task.getTitle()); assigned.setTaskDescription(task.getDescription()); - assigned.setTaskText(task.resolveTitle()); + assigned.setTaskText(task.getTitle()); assigned.setTaskMinutes(task.getMinutes()); assigned.setAssignedAt(LocalDateTime.now()); assigned.setAcceptDeadline(LocalDateTime.now().plusHours(1)); assigned.setStatus("PENDING"); assignedTaskRepository.save(assigned); - vote.setStatus("COMPLETED"); + vote.setActive(false); vote.setWinningTaskIndex(winnerIndex); communityTaskVoteRepository.save(vote); - // Lockee benachrichtigen sendMessage(lock.getLockee(), - "Die Community hat für deine Aufgabe abgestimmt: \"" + task.resolveTitle() + "\"", + "Die Community hat für deine Aufgabe abgestimmt: \"" + task.getTitle() + "\"", "/activelock.html?lockId=" + lock.getLockId()); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationController.java new file mode 100644 index 0000000..5d9dae6 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationController.java @@ -0,0 +1,126 @@ +package de.oaa.xxx.games.chastity.community; + +import java.security.Principal; +import java.time.LocalDateTime; +import java.util.UUID; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +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.RestController; + +import de.oaa.xxx.games.chastity.common.CodeCreator; +import de.oaa.xxx.user.UserRepository; + +@RestController +@RequestMapping("/games/chastity/community/verification") +@Transactional +public class CommunityVerificationController { + + private final CommunityVerificationRepository verificationRepository; + private final CommunityVerificationVoteRepository verificationVoteRepository; + private final UserRepository userRepository; + + public CommunityVerificationController(CommunityVerificationRepository verificationRepository, + CommunityVerificationVoteRepository verificationVoteRepository, UserRepository userRepository) { + this.verificationRepository = verificationRepository; + this.verificationVoteRepository = verificationVoteRepository; + this.userRepository = userRepository; + } + + @GetMapping("/{displayId}") + public ResponseEntity get(@PathVariable UUID displayId, Principal principal) { + var optional = verificationRepository.findById(displayId); + if (optional.isEmpty()) { + return ResponseEntity.noContent().build(); + } + var entity = optional.get(); + + boolean isOwnLock = false; + Boolean myVote = null; + if (principal != null) { + var userOpt = userRepository.findByEmail(principal.getName()); + if (userOpt.isPresent()) { + var myId = userOpt.get().getUserId(); + isOwnLock = myId.equals(entity.getLockeeId()); + if (!isOwnLock) { + myVote = verificationVoteRepository + .findByVerificationIdAndUserId(displayId, myId) + .map(CommunityVerificationVoteEntity::isUpvote) + .orElse(null); + } + } + } + + var dto = entity.toVerification(isOwnLock, myVote); + verificationVoteRepository.findAllByVerificationId(displayId).stream() + .map(CommunityVerificationVoteEntity::toVerificationVote) + .forEach(dto.votes()::add); + return ResponseEntity.ok(dto); + } + + @GetMapping("/new") + public ResponseEntity createVerification() { + var verification = new CommunityVerificationEntity(); + verification.setDisplayId(UUID.randomUUID()); + verification.setCode(CodeCreator.createAlphanumeric(6)); + verification.setCreatedAt(LocalDateTime.now()); + verificationRepository.save(verification); + return ResponseEntity.ok(verification.toVerification(true, null)); + } + + @PutMapping("/{displayId}") + public ResponseEntity update(@PathVariable UUID displayId, @RequestBody CommunityVerificationDTO dto, + Principal principal) { + var user = userRepository.findByEmail(principal.getName()).orElse(null); + if (user == null) { + return ResponseEntity.status(401).build(); + } + var entity = verificationRepository.findById(displayId).orElse(null); + if (entity == null) { + return ResponseEntity.notFound().build(); + } + if (entity.getCreatedAt().isBefore(LocalDateTime.now().minusHours(1))) { + return ResponseEntity.status(HttpStatus.GONE).build(); + } + if (dto.image() != null) { + entity.setImage(dto.image()); + } + verificationRepository.save(entity); + return ResponseEntity.ok().build(); + } + + @PostMapping("/{verificationId}/vote/") + public ResponseEntity addVote(@PathVariable UUID verificationId, @RequestBody CommunityVerificationVoteDTO dto, + Principal principal) { + var user = userRepository.findByEmail(principal.getName()).orElse(null); + if (user == null) return ResponseEntity.status(401).build(); + if (!verificationRepository.existsById(verificationId)) return ResponseEntity.notFound().build(); + + var vEntity = verificationRepository.findById(verificationId).orElse(null); + if (vEntity == null) return ResponseEntity.notFound().build(); + if (user.getUserId().equals(vEntity.getLockeeId())) return ResponseEntity.status(403).build(); + if (verificationVoteRepository.findByVerificationIdAndUserId(verificationId, user.getUserId()).isPresent()) { + return ResponseEntity.status(409).build(); + } + var vote = new CommunityVerificationVoteEntity(); + vote.setVoteId(UUID.randomUUID()); + vote.setVerificationId(verificationId); + vote.setUserId(user.getUserId()); + vote.setUpvote(dto.upvote()); + if (dto.upvote()) { + vEntity.setCountUpvotes(vEntity.getCountUpvotes() + 1); + } else { + vEntity.setCountDownvotes(vEntity.getCountDownvotes() + 1); + } + verificationVoteRepository.save(vote); + return ResponseEntity.accepted().build(); + } + +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationDTO.java new file mode 100644 index 0000000..e599dbe --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationDTO.java @@ -0,0 +1,10 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +public record CommunityVerificationDTO( + UUID displayId, String code, LocalDateTime verificationTime, + byte[] image, List votes, int upvotes, int downvotes, + boolean isOwnLock, Boolean myVote) {} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationEntity.java new file mode 100644 index 0000000..0421628 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationEntity.java @@ -0,0 +1,62 @@ +package de.oaa.xxx.games.chastity.community; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.UUID; + +import de.oaa.xxx.games.chastity.common.Verification; +import de.oaa.xxx.games.chastity.common.VerificationCommonDTO; +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@DiscriminatorValue("VERIFICATION") +public class CommunityVerificationEntity extends BaseCommunityDisplayEntity implements Verification { + + @Column(nullable = false) + private String code; + @Column(columnDefinition = "MEDIUMBLOB") + private byte[] image; + @Column + private int countUpvotes; + @Column + private int countDownvotes; + + @Override + public String getType() { return "VERIFICATION"; } + + public CommunityVerificationDTO toVerification(boolean isOwnLock, Boolean myVote) { + return new CommunityVerificationDTO(getDisplayId(), code, getCreatedAt(), image, new ArrayList<>(), countUpvotes, countDownvotes, isOwnLock, myVote); + } + + @Override + public LocalDate getVerificationDate() { + return getCreatedAt().toLocalDate(); + } + + @Override + public boolean isValid() { + return countUpvotes > countDownvotes; + } + + @Override + public VerificationCommonDTO toCommonVerification() { + return new VerificationCommonDTO(getDisplayId(), getLockId(), code, getCreatedAt(), image, getLockeeId(), + getKeyholderId()); + } + + @Override + public UUID getId() { + return getDisplayId(); + } + + @Override + public void setId(UUID id) { + setDisplayId(id); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationRepository.java new file mode 100644 index 0000000..8b194c3 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationRepository.java @@ -0,0 +1,20 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommunityVerificationRepository extends JpaRepository { + + org.springframework.data.domain.Page findAllByImageIsNotNull(Pageable pageable); + + java.util.List findByLockId(UUID lockId); + + java.util.List findByLockIdAndCreatedAtBetweenAndImageIsNotNull(UUID lockId, java.time.LocalDateTime from, java.time.LocalDateTime to); + + java.util.List findByLockIdAndCreatedAtBetweenAndImageIsNull(UUID lockId, java.time.LocalDateTime from, java.time.LocalDateTime to); + + org.springframework.data.domain.Page findByKeyholderIdIsNullAndCreatedAtBetweenAndImageIsNotNull( + java.time.LocalDateTime from, java.time.LocalDateTime to, org.springframework.data.domain.Pageable pageable); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteDTO.java new file mode 100644 index 0000000..c582a85 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteDTO.java @@ -0,0 +1,5 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.UUID; + +public record CommunityVerificationVoteDTO (UUID voteId, UUID userId, boolean upvote) {} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteEntity.java similarity index 67% rename from xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteEntity.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteEntity.java index 5e773cf..453f52b 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteEntity.java @@ -1,4 +1,4 @@ -package de.oaa.xxx.games.chastity.verification; +package de.oaa.xxx.games.chastity.community; import java.util.UUID; @@ -13,7 +13,7 @@ import lombok.Setter; @Setter @Entity @Table(name="verification_vote") -public class VerificationVoteEntity { +public class CommunityVerificationVoteEntity { @Id @Column @@ -25,7 +25,7 @@ public class VerificationVoteEntity { @Column(nullable = false) private boolean upvote; - public VerificationVoteDTO toVerificationVote() { - return new VerificationVoteDTO(voteId, userId, upvote); + public CommunityVerificationVoteDTO toVerificationVote() { + return new CommunityVerificationVoteDTO(voteId, userId, upvote); } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteRepository.java new file mode 100644 index 0000000..9474334 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/community/CommunityVerificationVoteRepository.java @@ -0,0 +1,17 @@ +package de.oaa.xxx.games.chastity.community; + +import java.util.List; +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CommunityVerificationVoteRepository extends JpaRepository { + + List findAllByVerificationId(UUID verificationId); + + java.util.Optional findByVerificationIdAndUserId(UUID verificationId, UUID userId); + + void deleteAllByVerificationId(UUID verificationId); +} + + diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceController.java similarity index 51% rename from xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceController.java index e2a683d..4ba0b84 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/cardlock/TaskCardController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceController.java @@ -1,53 +1,52 @@ -package de.oaa.xxx.games.chastity.cardlock; - -import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; -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.games.chastity.vote.CommunityTaskVoteEntryEntity; -import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteEntryRepository; -import de.oaa.xxx.games.chastity.vote.CommunityTaskVoteRepository; -import de.oaa.xxx.social.SystemMessageService; -import de.oaa.xxx.user.UserRepository; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; +package de.oaa.xxx.games.chastity.keyholder; import java.security.Principal; import java.time.LocalDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.oaa.xxx.games.chastity.cardlock.CardlockRepository; +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.SystemMessageService; +import de.oaa.xxx.user.UserRepository; @RestController -@RequestMapping("/task-card") -public class TaskCardController { +@RequestMapping("/games/chastity/keyholder/choices") +public class KeyholderTaskChoiceController { private final CardlockRepository cardlockRepository; private final UserRepository userRepository; private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; - private final CommunityTaskVoteRepository communityTaskVoteRepository; - private final CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository; private final AssignedTaskRepository assignedTaskRepository; private final SystemMessageService systemMessageService; - public TaskCardController(CardlockRepository cardlockRepository, + public KeyholderTaskChoiceController(CardlockRepository cardlockRepository, UserRepository userRepository, KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, - CommunityTaskVoteRepository communityTaskVoteRepository, - CommunityTaskVoteEntryRepository communityTaskVoteEntryRepository, AssignedTaskRepository assignedTaskRepository, SystemMessageService systemMessageService) { this.cardlockRepository = cardlockRepository; this.userRepository = userRepository; this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; - this.communityTaskVoteRepository = communityTaskVoteRepository; - this.communityTaskVoteEntryRepository = communityTaskVoteEntryRepository; this.assignedTaskRepository = assignedTaskRepository; this.systemMessageService = systemMessageService; } - + // ── Keyholder: ausstehende Aufgaben-Karten-Entscheidungen ───────────────── - - @GetMapping("/keyholder/choices") + @GetMapping @Transactional(readOnly = true) public ResponseEntity>> getPendingChoices(Principal principal) { var meOpt = userRepository.findByEmail(principal.getName()); @@ -59,7 +58,7 @@ public class TaskCardController { List> result = new ArrayList<>(); for (var lock : locks) { - var pending = keyholderTaskChoiceRepository.findByLockIdAndStatus(lock.getLockId(), "PENDING"); + var pending = keyholderTaskChoiceRepository.findByLockIdAndActiveTrue(lock.getLockId()); if (pending.isEmpty()) continue; var lockee = userRepository.findById(lock.getLockee()).orElse(null); @@ -81,7 +80,7 @@ public class TaskCardController { record PenaltyRequest(Integer penaltyFreezeMinutes, Integer penaltyRedCards) {} - @PostMapping("/keyholder/choices/{choiceId}/choose/{taskIndex}") + @PostMapping("/{choiceId}/choose/{taskIndex}") @Transactional public ResponseEntity chooseTask(@PathVariable UUID choiceId, @PathVariable int taskIndex, @@ -100,7 +99,7 @@ public class TaskCardController { var lock = lockOpt.get(); if (!myId.equals(lock.getKeyholder())) return ResponseEntity.status(403).build(); - if (!"PENDING".equals(choice.getStatus())) return ResponseEntity.status(409).build(); + if (!choice.isActive()) return ResponseEntity.status(409).build(); List tasks = lock.getTasks(); if (tasks == null || taskIndex < 0 || taskIndex >= tasks.size()) @@ -109,9 +108,9 @@ public class TaskCardController { Task task = tasks.get(taskIndex); AssignedTaskEntity assigned = new AssignedTaskEntity(); assigned.setLockId(lock.getLockId()); - assigned.setTaskTitle(task.resolveTitle()); + assigned.setTaskTitle(task.getTitle()); assigned.setTaskDescription(task.getDescription()); - assigned.setTaskText(task.resolveTitle()); + assigned.setTaskText(task.getTitle()); assigned.setTaskMinutes(task.getMinutes()); assigned.setAssignedAt(LocalDateTime.now()); assigned.setAcceptDeadline(LocalDateTime.now().plusHours(1)); @@ -122,7 +121,7 @@ public class TaskCardController { } assignedTaskRepository.save(assigned); - choice.setStatus("CHOSEN"); + choice.setActive(false); keyholderTaskChoiceRepository.save(choice); sendMessage(myId, lock.getLockee(), @@ -132,94 +131,6 @@ public class TaskCardController { return ResponseEntity.noContent().build(); } - // ── Community: aktive Abstimmungen ──────────────────────────────────────── - - @GetMapping("/community/votes") - @Transactional(readOnly = true) - public ResponseEntity>> getActiveVotes(Principal principal) { - var meOpt = userRepository.findByEmail(principal.getName()); - if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); - UUID myId = meOpt.get().getUserId(); - - var activeVotes = communityTaskVoteRepository.findByStatus("ACTIVE"); - List> result = new ArrayList<>(); - - for (var vote : activeVotes) { - if (vote.isTestLock()) continue; - var lockOpt = cardlockRepository.findById(vote.getLockId()); - if (lockOpt.isEmpty()) continue; - var lock = lockOpt.get(); - - var lockee = userRepository.findById(lock.getLockee()).orElse(null); - List tasks = lock.getTasks(); - if (tasks == null || tasks.isEmpty()) continue; - - List> taskList = buildTaskList(tasks); - - // Stimmenanzahl pro Task - List voteCounts = new ArrayList<>(); - for (int i = 0; i < tasks.size(); i++) { - voteCounts.add(communityTaskVoteEntryRepository - .countByVoteSessionIdAndTaskIndex(vote.getVoteSessionId(), i)); - } - - Integer myVote = communityTaskVoteEntryRepository.findByVoteSessionId(vote.getVoteSessionId()) - .stream().filter(e -> myId.equals(e.getVoterUserId())) - .map(CommunityTaskVoteEntryEntity::getTaskIndex).findFirst().orElse(null); - - boolean isOwnLock = lock.getLockee().equals(myId); - - Map m = new LinkedHashMap<>(); - m.put("voteSessionId", vote.getVoteSessionId().toString()); - m.put("lockId", lock.getLockId().toString()); - m.put("lockeeName", lockee != null ? lockee.getName() : ""); - m.put("createdAt", vote.getCreatedAt().toString()); - m.put("expiresAt", vote.getExpiresAt().toString()); - m.put("tasks", taskList); - m.put("voteCounts", voteCounts); - m.put("myVote", isOwnLock ? "own" : myVote); - m.put("isOwnLock", isOwnLock); - result.add(m); - } - return ResponseEntity.ok(result); - } - - @PostMapping("/community/votes/{voteSessionId}/vote/{taskIndex}") - @Transactional - public ResponseEntity castVote(@PathVariable UUID voteSessionId, - @PathVariable int taskIndex, - Principal principal) { - var meOpt = userRepository.findByEmail(principal.getName()); - if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); - UUID myId = meOpt.get().getUserId(); - - var voteOpt = communityTaskVoteRepository.findById(voteSessionId); - if (voteOpt.isEmpty()) return ResponseEntity.notFound().build(); - var vote = voteOpt.get(); - - if (!"ACTIVE".equals(vote.getStatus()) || vote.getExpiresAt().isBefore(LocalDateTime.now())) - return ResponseEntity.status(409).build(); - - var lockOpt = cardlockRepository.findById(vote.getLockId()); - if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); - var lock = lockOpt.get(); - - if (lock.getLockee().equals(myId)) return ResponseEntity.status(403).build(); - - if (lock.getTasks() == null || taskIndex < 0 || taskIndex >= lock.getTasks().size()) - return ResponseEntity.badRequest().build(); - - if (communityTaskVoteEntryRepository.existsByVoteSessionIdAndVoterUserId(voteSessionId, myId)) - return ResponseEntity.status(409).build(); - - CommunityTaskVoteEntryEntity entry = new CommunityTaskVoteEntryEntity(); - entry.setVoteSessionId(voteSessionId); - entry.setVoterUserId(myId); - entry.setTaskIndex(taskIndex); - communityTaskVoteEntryRepository.save(entry); - - return ResponseEntity.noContent().build(); - } // ── Helpers ─────────────────────────────────────────────────────────────── @@ -230,7 +141,7 @@ public class TaskCardController { Task t = tasks.get(i); Map m = new LinkedHashMap<>(); m.put("index", i); - m.put("title", t.resolveTitle()); + m.put("title", t.getTitle()); m.put("description", t.getDescription() != null ? t.getDescription() : ""); m.put("minutes", t.getMinutes() != null ? t.getMinutes() : 0); list.add(m); diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceEntity.java index f614a7a..8af9a18 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceEntity.java @@ -20,10 +20,12 @@ public class KeyholderTaskChoiceEntity { @Column(nullable = false) private UUID lockId; - /** PENDING | CHOSEN */ @Column(nullable = false) - private String status = "PENDING"; + private boolean active = true; @Column(nullable = false) private LocalDateTime createdAt; + + @Column + private LocalDateTime expiresAt; } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceRepository.java index 064dd33..4673b51 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceRepository.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceRepository.java @@ -1,9 +1,11 @@ package de.oaa.xxx.games.chastity.keyholder; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; import java.util.List; import java.util.UUID; public interface KeyholderTaskChoiceRepository extends JpaRepository { - List findByLockIdAndStatus(UUID lockId, String status); + List findByLockIdAndActiveTrue(UUID lockId); + List findByActiveTrueAndExpiresAtBefore(LocalDateTime time); } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceScheduler.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceScheduler.java new file mode 100644 index 0000000..fa76700 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderTaskChoiceScheduler.java @@ -0,0 +1,97 @@ +package de.oaa.xxx.games.chastity.keyholder; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import de.oaa.xxx.games.chastity.cardlock.CardlockRepository; +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.SystemMessageService; +import de.oaa.xxx.social.entity.MessageCause; + +@Component +public class KeyholderTaskChoiceScheduler { + + private static final Logger LOG = LoggerFactory.getLogger(KeyholderTaskChoiceScheduler.class); + + private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; + private final CardlockRepository cardlockRepository; + private final AssignedTaskRepository assignedTaskRepository; + private final SystemMessageService systemMessageService; + + public KeyholderTaskChoiceScheduler(KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + CardlockRepository cardlockRepository, + AssignedTaskRepository assignedTaskRepository, + SystemMessageService systemMessageService) { + this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; + this.cardlockRepository = cardlockRepository; + this.assignedTaskRepository = assignedTaskRepository; + this.systemMessageService = systemMessageService; + } + + @Scheduled(fixedDelay = 60_000) + @Transactional + public void processExpiredChoices() { + var expired = keyholderTaskChoiceRepository + .findByActiveTrueAndExpiresAtBefore(LocalDateTime.now()); + + for (var choice : expired) { + LOG.debug("Processing expired keyholder task choice {}", choice.getChoiceId()); + + var lockOpt = cardlockRepository.findById(choice.getLockId()); + if (lockOpt.isEmpty()) { + choice.setActive(false); + keyholderTaskChoiceRepository.save(choice); + continue; + } + var lock = lockOpt.get(); + List tasks = lock.getTasks(); + if (tasks == null || tasks.isEmpty()) { + choice.setActive(false); + keyholderTaskChoiceRepository.save(choice); + continue; + } + + int taskIndex = new Random().nextInt(tasks.size()); + Task task = tasks.get(taskIndex); + LOG.debug("Keyholder did not choose in time → random task index {}", taskIndex); + + AssignedTaskEntity assigned = new AssignedTaskEntity(); + assigned.setLockId(lock.getLockId()); + assigned.setTaskTitle(task.getTitle()); + assigned.setTaskDescription(task.getDescription()); + assigned.setTaskText(task.getTitle()); + assigned.setTaskMinutes(task.getMinutes()); + assigned.setAssignedAt(LocalDateTime.now()); + assigned.setAcceptDeadline(LocalDateTime.now().plusHours(1)); + assigned.setStatus("PENDING"); + assignedTaskRepository.save(assigned); + + choice.setActive(false); + keyholderTaskChoiceRepository.save(choice); + + sendMessage(lock.getLockee(), + "Dein Keyholder hat nicht rechtzeitig gewählt – eine zufällige Aufgabe wurde vergeben: \"" + task.getTitle() + "\"", + "/activelock.html?lockId=" + lock.getLockId()); + + if (lock.getKeyholder() != null) { + sendMessage(lock.getKeyholder(), + "Du hast die Aufgabenwahl für \"" + (lock.getName() != null ? lock.getName() : "ein Schloss") + "\" nicht rechtzeitig getroffen. Eine zufällige Aufgabe wurde vergeben.", + "/keyholder.html"); + } + } + } + + private void sendMessage(UUID toId, String text, String targetUrl) { + systemMessageService.send(toId, toId, text, targetUrl, MessageCause.GAME_STATE); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationEntity.java new file mode 100644 index 0000000..d7ea4d6 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationEntity.java @@ -0,0 +1,64 @@ +package de.oaa.xxx.games.chastity.keyholder; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.UUID; + +import de.oaa.xxx.games.chastity.common.Verification; +import de.oaa.xxx.games.chastity.common.VerificationCommonDTO; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +@Table(name = "keyholder_verification") +public class KeyholderVerificationEntity implements Verification { + + @Id + @Column + private UUID verificationId; + @Column(nullable = false) + private UUID lockId; + @Column(nullable = false) + private String code; + @Column(nullable = false) + private LocalDateTime createdAt; + @Column(columnDefinition = "MEDIUMBLOB") + private byte[] image; + @Column(nullable = false) + private UUID lockeeId; + @Column(nullable = false) + private UUID keyholderId; + @Column + private boolean accepted; + + @Override + public LocalDate getVerificationDate() { + return createdAt.toLocalDate(); + } + + @Override + public boolean isValid() { + return accepted; + } + + @Override + public VerificationCommonDTO toCommonVerification() { + return new VerificationCommonDTO(verificationId, lockId, code, createdAt, image, lockeeId, keyholderId); + } + + @Override + public UUID getId() { + return verificationId; + } + + @Override + public void setId(UUID id) { + setVerificationId(id); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationRepository.java new file mode 100644 index 0000000..d1d3042 --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/keyholder/KeyholderVerificationRepository.java @@ -0,0 +1,10 @@ +package de.oaa.xxx.games.chastity.keyholder; + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface KeyholderVerificationRepository extends JpaRepository{ + + java.util.List findByLockId(UUID lockId); +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/TTLockControl.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/TTLockControl.java index 771184b..43a08a6 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/TTLockControl.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/TTLockControl.java @@ -2,12 +2,12 @@ package de.oaa.xxx.games.chastity.lockcontroll; public class TTLockControl extends LockControl { + // private static final String BASE_URL = "https://euapi.ttlock.com/"; + public TTLockControl() { super(new NoInteractionCallback()); } - private static final String BASE_URL = "https://euapi.ttlock.com/"; - @Override public boolean init() { // TODO Auto-generated method stub @@ -25,8 +25,5 @@ public class TTLockControl extends LockControl { // TODO Auto-generated method stub return false; } - - - - + } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/UnlockcodeLockControl.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/UnlockcodeLockControl.java index a0c86b2..e052505 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/UnlockcodeLockControl.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockcontroll/UnlockcodeLockControl.java @@ -20,7 +20,7 @@ public class UnlockcodeLockControl extends LockControl { @Override public boolean lock() { - var code = CodeCreator.createAlphanumericCode(callback.getUnlockcodeLenght()); + var code = CodeCreator.createNumeric(callback.getUnlockcodeLenght()); callback.setUnlockCode(code); return true; } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockee/LockeeInvitationController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockee/LockeeInvitationController.java index 4fcd244..c54f178 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockee/LockeeInvitationController.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/lockee/LockeeInvitationController.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.UUID; import org.springframework.beans.factory.annotation.Value; @@ -21,7 +22,12 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import de.oaa.xxx.games.chastity.cardlock.CardLockEntity; import de.oaa.xxx.games.chastity.cardlock.CardlockRepository; +import de.oaa.xxx.games.chastity.common.BaseLockRepository; +import de.oaa.xxx.games.chastity.common.CodeCreator; +import de.oaa.xxx.games.chastity.timelock.TimeLockEntity; +import de.oaa.xxx.games.chastity.timelock.TimeLockRepository; import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; @@ -31,6 +37,8 @@ public class LockeeInvitationController { private final LockeeInvitationRepository lockeeInvitationRepository; private final CardlockRepository cardlockRepository; + private final BaseLockRepository baseLockRepository; + private final TimeLockRepository timeLockRepository; private final UserRepository userRepository; private final SystemMessageService systemMessageService; @@ -41,10 +49,14 @@ public class LockeeInvitationController { public LockeeInvitationController(LockeeInvitationRepository lockeeInvitationRepository, CardlockRepository cardlockRepository, + BaseLockRepository baseLockRepository, + TimeLockRepository timeLockRepository, UserRepository userRepository, SystemMessageService systemMessageService) { this.lockeeInvitationRepository = lockeeInvitationRepository; this.cardlockRepository = cardlockRepository; + this.baseLockRepository = baseLockRepository; + this.timeLockRepository = timeLockRepository; this.userRepository = userRepository; this.systemMessageService = systemMessageService; } @@ -59,6 +71,12 @@ public class LockeeInvitationController { return sb.toString(); } + private int randomBetween(Integer min, Integer max) { + if (max == null) max = 60; + if (min == null || min >= max) return max; + return min + new Random().nextInt(max - min); + } + @GetMapping("/invitations/mine") public ResponseEntity>> getMyInvitations(Principal principal) { var meOpt = userRepository.findByEmail(principal.getName()); @@ -68,7 +86,7 @@ public class LockeeInvitationController { var invitations = lockeeInvitationRepository.findByLockeeUserId(myId); List> result = new ArrayList<>(); for (var inv : invitations) { - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); if (lockOpt.isEmpty()) continue; var lock = lockOpt.get(); if (lock.getStartTime() != null) continue; // already accepted @@ -97,7 +115,7 @@ public class LockeeInvitationController { var invitations = lockeeInvitationRepository.findByKeyholderUserId(myId); List> result = new ArrayList<>(); for (var inv : invitations) { - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); if (lockOpt.isEmpty()) continue; var lock = lockOpt.get(); if (lock.getStartTime() != null) continue; // already accepted @@ -130,12 +148,12 @@ public class LockeeInvitationController { var inv = invOpt.get(); if (!inv.getKeyholderUserId().equals(myId)) return ResponseEntity.status(403).build(); - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); lockeeInvitationRepository.delete(inv); if (lockOpt.isPresent()) { var lock = lockOpt.get(); - cardlockRepository.delete(lock); + baseLockRepository.delete(lock); 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.", @@ -156,7 +174,7 @@ public class LockeeInvitationController { var inv = invOpt.get(); if (!inv.getLockeeUserId().equals(myId)) return ResponseEntity.status(403).build(); - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); var lock = lockOpt.get(); @@ -173,18 +191,18 @@ public class LockeeInvitationController { result.put("createdAt", inv.getCreatedAt().toString()); result.put("detailsVisible", inv.isDetailsVisible()); - if (inv.isDetailsVisible() && lock.getInitialCards() != null) { - Map cardCounts = lock.getInitialCards().stream() + if (inv.isDetailsVisible() && lock instanceof CardLockEntity cardLock && cardLock.getInitialCards() != null) { + Map cardCounts = cardLock.getInitialCards().stream() .collect(java.util.stream.Collectors.groupingBy( c -> c.name(), java.util.stream.Collectors.counting())); result.put("cardCounts", cardCounts); - result.put("pickEveryMinute", lock.getPickEveryMinute()); - result.put("accumulatePicks", lock.isAccumulatePicks()); - result.put("showRemainingCards", lock.isShowRemainingCards()); - result.put("hygineOpeningEveryMinites", lock.getHygineOpeningEveryMinites()); - result.put("hygineOpeningDurationMinutes", lock.getHygineOpeningDurationMinutes()); - result.put("requiresVerification", lock.isRequiresVerification()); - result.put("taskCount", lock.getTasks() != null ? lock.getTasks().size() : 0); + result.put("pickEveryMinute", cardLock.getPickEveryMinute()); + result.put("accumulatePicks", cardLock.isAccumulatePicks()); + result.put("showRemainingCards", cardLock.isShowRemainingCards()); + result.put("hygineOpeningEveryMinites", cardLock.getHygineOpeningEveryMinites()); + result.put("hygineOpeningDurationMinutes", cardLock.getHygineOpeningDurationMinutes()); + result.put("requiresVerification", cardLock.isRequiresVerification()); + result.put("taskCount", cardLock.getTasks() != null ? cardLock.getTasks().size() : 0); } return ResponseEntity.ok(result); @@ -207,30 +225,52 @@ public class LockeeInvitationController { var inv = invOpt.get(); if (!inv.getLockeeUserId().equals(myId)) return ResponseEntity.status(403).build(); - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); var lock = lockOpt.get(); if (lock.getStartTime() != null) return ResponseEntity.status(409).body(Map.of("error", "already_accepted")); - if (cardlockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(myId)) + + if (cardlockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(myId) + || timeLockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(myId)) return ResponseEntity.status(409).body(Map.of("error", "active_lock_exists")); int codeLines = (req.unlockCodeLines() != null && req.unlockCodeLines() >= 1) ? req.unlockCodeLines() : 5; - String unlockCode = generateUnlockCode(codeLines); - LocalDateTime now = LocalDateTime.now(); - lock.setStartTime(now); - lock.setUnlockCode(unlockCode); - lock.setUnlockCodeLength(codeLines); - lock.setAvailableCards(new ArrayList<>(lock.getInitialCards())); - lock.setOpenPicks(0); - lock.setNextCardIn(now.plusMinutes(lock.getPickEveryMinute())); - if (lock.getHygineOpeningEveryMinites() != null) { - lock.setLastHygineOpening(now); + + String unlockCode; + String lockName; + + if (lock instanceof CardLockEntity cardLock) { + unlockCode = generateUnlockCode(codeLines); + cardLock.setStartTime(now); + cardLock.setUnlockCode(unlockCode); + cardLock.setUnlockCodeLength(codeLines); + cardLock.setAvailableCards(new ArrayList<>(cardLock.getInitialCards())); + cardLock.setOpenPicks(0); + cardLock.setNextCardIn(now.plusMinutes(cardLock.getPickEveryMinute())); + if (cardLock.getHygineOpeningEveryMinites() != null) { + cardLock.setLastHygineOpening(now); + } + cardlockRepository.save(cardLock); + lockName = cardLock.getName() != null && !cardLock.getName().isBlank() ? cardLock.getName() : "Unbenanntes Lock"; + } else if (lock instanceof TimeLockEntity timeLock) { + unlockCode = CodeCreator.createNumeric(codeLines); + int unlockMinutes = randomBetween(timeLock.getMinTimeInMinutes(), timeLock.getMaxTimeInMinutes()); + timeLock.setStartTime(now); + timeLock.setUnlockTime(now.plusMinutes(unlockMinutes)); + timeLock.setUnlockCode(unlockCode); + timeLock.setUnlockCodeLength(codeLines); + if (timeLock.getHygineOpeningEveryMinites() != null) { + timeLock.setLastHygineOpening(now); + } + timeLockRepository.save(timeLock); + lockName = timeLock.getName() != null && !timeLock.getName().isBlank() ? timeLock.getName() : "Unbenanntes Lock"; + } else { + return ResponseEntity.status(500).build(); } - cardlockRepository.save(lock); + lockeeInvitationRepository.delete(inv); - 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", de.oaa.xxx.social.entity.MessageCause.INVITATION); @@ -254,12 +294,12 @@ public class LockeeInvitationController { var inv = invOpt.get(); if (!inv.getLockeeUserId().equals(myId)) return ResponseEntity.status(403).build(); - var lockOpt = cardlockRepository.findById(inv.getLockId()); + var lockOpt = baseLockRepository.findById(inv.getLockId()); lockeeInvitationRepository.delete(inv); if (lockOpt.isPresent()) { var lock = lockOpt.get(); - cardlockRepository.delete(lock); + baseLockRepository.delete(lock); 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.", diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryEntity.java deleted file mode 100644 index 341e214..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryEntity.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.oaa.xxx.games.chastity.pillory; - -import java.time.LocalDateTime; -import java.util.UUID; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -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 = "pillory") -public class PilloryEntity { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - @Setter(lombok.AccessLevel.NONE) - private UUID pilloryId; - @Column(nullable = false) - private UUID lockId; - @Column(nullable = false) - private UUID lockeeUserId; - @Column(nullable = false) - private LocalDateTime createdAt; - @Column - private PilloryReason reason; -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryReason.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryReason.java deleted file mode 100644 index 4fc8e00..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryReason.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.oaa.xxx.games.chastity.pillory; - -public enum PilloryReason { - - HYGIENE_OPENING_EXEEDED, - KEYHOLDER_DESCESSION; -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryRepository.java deleted file mode 100644 index 959db84..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/pillory/PilloryRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.oaa.xxx.games.chastity.pillory; - -import java.util.UUID; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface PilloryRepository extends JpaRepository { - -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/tasks/Task.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/tasks/Task.java index abe1557..fcd0d3f 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/tasks/Task.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/tasks/Task.java @@ -11,16 +11,6 @@ public class Task { private String description; private Integer minutes; - /** @deprecated Backward-Compat – alte Einträge ohne title/description. Nur lesen, nicht setzen. */ - private String text; - - /** Gibt den anzeigbaren Titel zurück – fällt auf altes text-Feld zurück. */ - public String resolveTitle() { - if (title != null && !title.isBlank()) return title; - if (text != null && !text.isBlank()) return text; - return "Aufgabe"; - } - @Override public String toString() { return "Task[title=" + title + ", minutes=" + minutes + "]"; diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockController.java new file mode 100644 index 0000000..9b4e38a --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockController.java @@ -0,0 +1,645 @@ +package de.oaa.xxx.games.chastity.timelock; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.Principal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.imageio.ImageIO; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +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.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.multipart.MultipartFile; + +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteEntity; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationEntity; +import de.oaa.xxx.games.chastity.keyholder.KeyholderInvitationRepository; +import de.oaa.xxx.games.chastity.lockcontroll.LockControllType; +import de.oaa.xxx.games.chastity.lockee.LockeeInvitationEntity; +import de.oaa.xxx.games.chastity.lockee.LockeeInvitationRepository; +import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry; +import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; +import de.oaa.xxx.social.SystemMessageService; +import de.oaa.xxx.user.UserRepository; + +@RestController +@RequestMapping("/keyholder") +public class TimeLockController { + + private final TimeLockRepository timeLockRepository; + private final TimeLockTemplateRepository templateRepository; + private final UserRepository userRepository; + private final KeyholderInvitationRepository invitationRepository; + private final LockeeInvitationRepository lockeeInvitationRepository; + private final SystemMessageService systemMessageService; + private final TimeLockServiceFactory timeLockServiceFactory; + private final CommunityVerificationRepository verificationRepository; + private final CommunityVerificationVoteRepository verificationVoteRepository; + + public TimeLockController(TimeLockRepository timeLockRepository, + TimeLockTemplateRepository templateRepository, + UserRepository userRepository, + KeyholderInvitationRepository invitationRepository, + LockeeInvitationRepository lockeeInvitationRepository, + SystemMessageService systemMessageService, + TimeLockServiceFactory timeLockServiceFactory, + CommunityVerificationRepository verificationRepository, + CommunityVerificationVoteRepository verificationVoteRepository) { + this.timeLockRepository = timeLockRepository; + this.templateRepository = templateRepository; + this.userRepository = userRepository; + this.invitationRepository = invitationRepository; + this.lockeeInvitationRepository = lockeeInvitationRepository; + this.systemMessageService = systemMessageService; + this.timeLockServiceFactory = timeLockServiceFactory; + this.verificationRepository = verificationRepository; + this.verificationVoteRepository = verificationVoteRepository; + } + + // ── Erstellen ──────────────────────────────────────────────────────────────── + + record CreateTimeLockRequest( + UUID templateId, + UUID lockeeUserId, + boolean lockeeDetailsVisible, + UUID keyholder, + boolean testLock, + Integer unlockCodeLength, + LockControllType controllType + ) {} + + @PostMapping("/timelock") + @Transactional + public ResponseEntity> createTimeLock( + @RequestBody CreateTimeLockRequest req, Principal principal) { + + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + var me = meOpt.get(); + UUID myId = me.getUserId(); + + var templateOpt = templateRepository.findById(req.templateId()); + if (templateOpt.isEmpty()) return ResponseEntity.badRequest().build(); + var template = templateOpt.get(); + + int codeLen = (req.unlockCodeLength() != null && req.unlockCodeLength() >= 1) ? req.unlockCodeLength() : 5; + boolean friendLockee = req.lockeeUserId() != null && !req.lockeeUserId().equals(myId); + + if (friendLockee) { + var lockeeOpt = userRepository.findById(req.lockeeUserId()); + if (lockeeOpt.isEmpty()) return ResponseEntity.badRequest().build(); + var lockee = lockeeOpt.get(); + + TimeLockEntity lock = buildBaseEntity(template, myId, req.lockeeUserId(), false); + lock.setStartTime(null); + lock.setUnlockTime(null); + timeLockRepository.save(lock); + + String token = UUID.randomUUID().toString().replace("-", ""); + LockeeInvitationEntity inv = new LockeeInvitationEntity(); + inv.setLockId(lock.getLockId()); + inv.setLockeeUserId(lockee.getUserId()); + inv.setKeyholderUserId(myId); + inv.setToken(token); + inv.setCreatedAt(LocalDateTime.now()); + inv.setDetailsVisible(req.lockeeDetailsVisible()); + lockeeInvitationRepository.save(inv); + + String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock"; + systemMessageService.send(myId, lockee.getUserId(), + me.getName() + " hat dich als Lockee für das Lock „" + lockName + "\" eingeladen.", + "/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION); + + return ResponseEntity.ok(Map.of( + "lockId", lock.getLockId().toString(), + "lockeeInvitationSent", true)); + } + + if (timeLockRepository.existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(myId)) + return ResponseEntity.status(409).body(Map.of("error", "active_lock_exists")); + + TimeLockAdditionalSettings settings = new TimeLockAdditionalSettings( + req.controllType() != null ? req.controllType() : LockControllType.UNLOCK_CODE, + myId, req.keyholder(), req.testLock(), codeLen); + TimeLockEntity lock = new TimeLockEntity(); + timeLockServiceFactory.create(lock).init(template, settings); + timeLockRepository.save(lock); // Sicherstellen dass auch TRUST-Locks persistiert sind + + boolean keyholderPending = false; + if (req.keyholder() != null) { + var khOpt = userRepository.findById(req.keyholder()); + if (khOpt.isPresent()) { + var kh = khOpt.get(); + KeyholderInvitationEntity inv = new KeyholderInvitationEntity(); + inv.setLockId(lock.getLockId()); + inv.setKeyholderUserId(kh.getUserId()); + inv.setLockeeUserId(myId); + inv.setToken(UUID.randomUUID().toString().replace("-", "")); + inv.setCreatedAt(LocalDateTime.now()); + invitationRepository.save(inv); + + String lockName = template.getName() != null ? template.getName() : "Unbenanntes Lock"; + systemMessageService.send(myId, kh.getUserId(), + me.getName() + " hat dich als Keyholder*In für das Lock „" + lockName + "\" eingeladen.", + "/einladungen.html", de.oaa.xxx.social.entity.MessageCause.INVITATION); + + keyholderPending = true; + } + } + + return ResponseEntity.ok(Map.of( + "lockId", lock.getLockId().toString(), + "unlockCode", lock.getUnlockCode() != null ? lock.getUnlockCode() : "", + "keyholderPending", keyholderPending)); + } + + // ── State abrufen ──────────────────────────────────────────────────────────── + + @GetMapping("/timelock/{lockId}") + @Transactional + public ResponseEntity> getTimeLock(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + + LocalDateTime now = LocalDateTime.now(); + + // Periodic check (daily violations) + TimeLockService service = timeLockServiceFactory.create(l); + service.check(); + + // Auto-unfreeze when frozenUntil has passed + if (l.getFrozenUntil() != null && l.getFrozenUntil().isBefore(now) && l.getFrozenFrom() != null) { + service.unfreeze(); + l.setFrozenFrom(null); + l.setFrozenUntil(null); + timeLockRepository.save(l); + } + + // Emergency auto-unlock after 1h + if (l.getEmergencyUnlockRequestedAt() != null && !l.isKeyholderRequestedUnlock() + && l.getEmergencyUnlockRequestedAt().isBefore(now.minusHours(1))) { + l.setEmergencyAutoUnlocked(true); + l.setKeyholderRequestedUnlock(true); + timeLockRepository.save(l); + } + + boolean timeUp = l.getUnlockTime() != null && l.getUnlockTime().isBefore(now); + boolean isFrozen = l.getFrozenFrom() != null + && (l.getFrozenUntil() == null || l.getFrozenUntil().isAfter(now)); + + // Hygiene state + boolean hygieneEnabled = l.getHygineOpeningEveryMinites() != null; + boolean hygieneOpeningDue = false; + long hygieneSecondsRemaining = 0; + boolean hygieneOpeningActive = l.getTempOpeningTime() != null + && TempOpeningReason.HYGIENE == l.getTempOpeningReason(); + if (hygieneEnabled && !hygieneOpeningActive) { + LocalDateTime lastH = l.getLastHygineOpening(); + if (lastH == null) { + hygieneOpeningDue = true; + } else { + LocalDateTime nextH = lastH.plusMinutes(l.getHygineOpeningEveryMinites()); + long secs = ChronoUnit.SECONDS.between(now, nextH); + if (secs <= 0) hygieneOpeningDue = true; + else hygieneSecondsRemaining = secs; + } + } + + // Spin wheel state + boolean spinEnabled = l.getSpinsEveryMinutes() != null + && l.getSpinningWheelEntries() != null && !l.getSpinningWheelEntries().isEmpty(); + boolean spinDue = false; + String nextSpinIn = null; + if (spinEnabled) { + List times = l.getSpinningWheelTimes(); + if (times == null || times.isEmpty()) { + spinDue = true; + } else { + LocalDateTime next = times.get(times.size() - 1).plusMinutes(l.getSpinsEveryMinutes()); + if (next.isBefore(now)) spinDue = true; + else nextSpinIn = next.toString(); + } + } + + // Task timing state + boolean taskTimingEnabled = l.getTaskEveryMinutes() != null; + String nextTaskIn = null; + if (taskTimingEnabled && l.getCurrentTask() == null) { + List times = l.getTaskTimes(); + LocalDateTime next; + if (times == null || times.isEmpty()) { + next = l.getStartTime() != null + ? l.getStartTime().plusMinutes(l.getTaskEveryMinutes()) : null; + } else { + next = times.get(times.size() - 1).plusMinutes(l.getTaskEveryMinutes()); + } + if (next != null && next.isAfter(now)) nextTaskIn = next.toString(); + } + + // Keyholder info + boolean keyholderInvitationPending = + l.getKeyholder() == null && !invitationRepository.findByLockId(lockId).isEmpty(); + + // Verification state + boolean verificationDue = false; + String verificationPendingId = null; + String verificationPendingCode = null; + long verificationUpvotes = 0; + long verificationDownvotes = 0; + if (l.isRequiresVerification()) { + LocalDateTime todayStart = LocalDate.now().atStartOfDay(); + LocalDateTime todayEnd = todayStart.plusDays(1); + var completed = verificationRepository + .findByLockIdAndCreatedAtBetweenAndImageIsNotNull(lockId, todayStart, todayEnd); + if (!completed.isEmpty()) { + var todayV = completed.get(0); + var votes = verificationVoteRepository.findAllByVerificationId(todayV.getDisplayId()); + verificationUpvotes = votes.stream().filter(CommunityVerificationVoteEntity::isUpvote).count(); + verificationDownvotes = votes.stream().filter(v -> !v.isUpvote()).count(); + } else { + verificationDue = true; + var pending = verificationRepository + .findByLockIdAndCreatedAtBetweenAndImageIsNull(lockId, todayStart, todayEnd); + if (!pending.isEmpty()) { + verificationPendingId = pending.get(0).getDisplayId().toString(); + verificationPendingCode = pending.get(0).getCode(); + } + } + } + + Map result = new LinkedHashMap<>(); + result.put("lockId", l.getLockId().toString()); + result.put("name", l.getName() != null ? l.getName() : ""); + result.put("testLock", l.isTestLock()); + result.put("startTime", l.getStartTime() != null ? l.getStartTime().toString() : null); + result.put("endTimeVisible", l.isEndTimeVisible()); + result.put("timeUp", timeUp); + result.put("isFrozen", isFrozen); + result.put("frozenUntil", l.getFrozenUntil() != null ? l.getFrozenUntil().toString() : null); + + // Only expose unlock time if end time is visible OR time is up + if (l.isEndTimeVisible() || timeUp) { + result.put("unlockTime", l.getUnlockTime() != null ? l.getUnlockTime().toString() : null); + } else { + result.put("unlockTime", null); + } + + result.put("currentTask", l.getCurrentTask()); + result.put("currentTaskDescription", l.getCurrentTaskDescription()); + result.put("taskUntil", l.getTaskUntil() != null ? l.getTaskUntil().toString() : null); + + result.put("spinEnabled", spinEnabled); + result.put("spinDue", spinDue); + result.put("nextSpinIn", nextSpinIn); + + result.put("taskTimingEnabled", taskTimingEnabled); + result.put("nextTaskIn", nextTaskIn); + + result.put("hygieneEnabled", hygieneEnabled); + result.put("hygieneOpeningDue", hygieneOpeningDue); + result.put("hygieneSecondsRemaining", hygieneSecondsRemaining); + result.put("hygieneOpeningActive", hygieneOpeningActive); + result.put("hygieneOpeningStarted", l.getTempOpeningTime() != null ? l.getTempOpeningTime().toString() : null); + result.put("hygieneDurationMinutes", l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 0); + + result.put("verificationRequired", l.isRequiresVerification()); + result.put("verificationDue", verificationDue); + result.put("verificationPendingId", verificationPendingId); + result.put("verificationPendingCode", verificationPendingCode); + result.put("verificationUpvotes", verificationUpvotes); + result.put("verificationDownvotes", verificationDownvotes); + + result.put("hasKeyholder", l.getKeyholder() != null); + result.put("keyholderInvitationPending", keyholderInvitationPending); + if (l.getKeyholder() != null) { + userRepository.findById(l.getKeyholder()).ifPresent(kh -> { + result.put("keyholderName", kh.getName()); + result.put("keyholderUserId", kh.getUserId().toString()); + result.put("keyholderProfilePic", kh.getProfilePicture()); + }); + } + + result.put("keyholderRequestedUnlock", l.isKeyholderRequestedUnlock()); + if (l.isKeyholderRequestedUnlock() || l.isTestLock()) { + result.put("unlockCode", l.getUnlockCode() != null ? l.getUnlockCode() : ""); + } + result.put("emergencyUnlockRequested", l.getEmergencyUnlockRequestedAt() != null); + + return ResponseEntity.ok(result); + } + + // ── Glücksrad drehen ───────────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/spin") + @Transactional + public ResponseEntity> spin(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + if (l.getUnlockTime() == null) return ResponseEntity.status(409).build(); // not started + if (l.getSpinningWheelEntries() == null || l.getSpinningWheelEntries().isEmpty()) + return ResponseEntity.status(409).build(); + + LocalDateTime now = LocalDateTime.now(); + boolean isFrozen = l.getFrozenFrom() != null + && (l.getFrozenUntil() == null || l.getFrozenUntil().isAfter(now)); + if (isFrozen) return ResponseEntity.status(409).body(Map.of("error", "frozen")); + if (TempOpeningReason.HYGIENE == l.getTempOpeningReason()) + return ResponseEntity.status(409).body(Map.of("error", "hygiene_opening")); + + // Check spin is due + List spinTimes = l.getSpinningWheelTimes(); + if (spinTimes != null && !spinTimes.isEmpty() && l.getSpinsEveryMinutes() != null) { + LocalDateTime next = spinTimes.get(spinTimes.size() - 1).plusMinutes(l.getSpinsEveryMinutes()); + if (next.isAfter(now)) return ResponseEntity.status(409).body(Map.of("error", "not_due")); + } + + TimeLockService service = timeLockServiceFactory.create(l); + SpinningWheelEntry entry = service.spinWheel(); + if (entry == null) return ResponseEntity.status(409).body(Map.of("error", "no_entry")); + + // Record spin time + if (l.getSpinningWheelTimes() == null) l.setSpinningWheelTimes(new ArrayList<>()); + l.getSpinningWheelTimes().add(now); + timeLockRepository.save(l); + + Map result = new LinkedHashMap<>(); + result.put("type", entry.getType().name()); + result.put("intVal", entry.getIntVal()); + result.put("stringVal", entry.getStringVal()); + // Include updated lock time fields + result.put("newUnlockTime", l.getUnlockTime() != null ? l.getUnlockTime().toString() : null); + result.put("newFrozenUntil", l.getFrozenUntil() != null ? l.getFrozenUntil().toString() : null); + result.put("isFrozen", l.getFrozenFrom() != null); + result.put("currentTask", l.getCurrentTask()); + return ResponseEntity.ok(result); + } + + // ── Aufgabe erledigt ────────────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/task/done") + @Transactional + public ResponseEntity taskDone(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + if (l.getCurrentTask() == null) return ResponseEntity.status(409).build(); + + // Check task timer hasn't expired (still locked) + if (l.getTaskUntil() != null && l.getTaskUntil().isAfter(LocalDateTime.now())) + return ResponseEntity.status(409).body(null); + + timeLockServiceFactory.create(l).clearTask(); + timeLockRepository.save(l); + return ResponseEntity.noContent().build(); + } + + // ── Hygiene-Öffnung starten ─────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/hygiene/start") + @Transactional + public ResponseEntity> startHygiene(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + if (l.getHygineOpeningEveryMinites() == null) return ResponseEntity.status(409).build(); + if (l.getTempOpeningTime() != null) return ResponseEntity.status(409).body(Map.of("error", "already_open")); + + TimeLockService service = timeLockServiceFactory.create(l); + service.startHygieneOpening(); + + return ResponseEntity.ok(Map.of( + "unlockCode", l.getUnlockCode() != null ? l.getUnlockCode() : "", + "durationMinutes", l.getHygineOpeningDurationMinutes() != null ? l.getHygineOpeningDurationMinutes() : 0, + "openedAt", l.getTempOpeningTime() != null ? l.getTempOpeningTime().toString() : "")); + } + + // ── Hygiene-Öffnung beenden ─────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/hygiene/end") + @Transactional + public ResponseEntity> endHygiene(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + if (l.getTempOpeningTime() == null || TempOpeningReason.HYGIENE != l.getTempOpeningReason()) + return ResponseEntity.status(409).build(); + + TimeLockService service = timeLockServiceFactory.create(l); + String newCode = service.endHygieneOpening(); + + return ResponseEntity.ok(Map.of( + "newUnlockCode", newCode, + "newUnlockTime", l.getUnlockTime() != null ? l.getUnlockTime().toString() : "")); + } + + // ── Verifikation starten ───────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/verification/start") + @Transactional + public ResponseEntity> startVerification(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + + var dto = timeLockServiceFactory.create(l).startVerification(); + return ResponseEntity.ok(Map.of("verificationId", dto.verficationId().toString(), "code", dto.code())); + } + + // ── Verifikation abschließen ────────────────────────────────────────────────── + + @PostMapping(value = "/timelock/{lockId}/verification/{verificationId}/complete", + consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Transactional + public ResponseEntity completeVerification( + @PathVariable UUID lockId, + @PathVariable UUID verificationId, + @RequestParam MultipartFile image, + Principal principal) throws IOException { + + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + + boolean found = timeLockServiceFactory.create(l).completeVerification(verificationId, scaleImage(image.getBytes(), 1024)); + if (!found) return ResponseEntity.notFound().build(); + + if (l.getKeyholder() != null) { + systemMessageService.send(myId, l.getKeyholder(), + "📸 " + meOpt.get().getName() + " hat eine Verifikation eingereicht.", + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.GAME_STATE); + } + return ResponseEntity.noContent().build(); + } + + // ── Lock beenden (Zeit abgelaufen / Test-Lock) ──────────────────────────────── + + @DeleteMapping("/timelock/{lockId}") + @Transactional + public ResponseEntity endLock(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + + LocalDateTime now = LocalDateTime.now(); + boolean timeUp = l.getUnlockTime() != null && l.getUnlockTime().isBefore(now); + boolean isFrozen = l.getFrozenFrom() != null + && (l.getFrozenUntil() == null || l.getFrozenUntil().isAfter(now)); + + if (!l.isTestLock() && !timeUp && !l.isKeyholderRequestedUnlock()) { + return ResponseEntity.status(409).build(); // Not yet unlockable + } + if (isFrozen && !l.isTestLock()) { + return ResponseEntity.status(409).body(null); // Frozen + } + + TimeLockService service = timeLockServiceFactory.create(l); + service.unlock(l.getUnlockCode()); + + // Clean up verifications + var verifications = verificationRepository.findByLockId(lockId); + verifications.forEach(v -> verificationVoteRepository.deleteAllByVerificationId(v.getDisplayId())); + verificationRepository.deleteAll(verifications); + invitationRepository.deleteByLockId(lockId); + timeLockRepository.deleteById(lockId); + + return ResponseEntity.noContent().build(); + } + + // ── Notfall-Entsperrung ─────────────────────────────────────────────────────── + + @PostMapping("/timelock/{lockId}/emergency-unlock") + @Transactional + public ResponseEntity requestEmergencyUnlock(@PathVariable UUID lockId, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + var me = meOpt.get(); + UUID myId = me.getUserId(); + + var lockOpt = timeLockRepository.findById(lockId); + if (lockOpt.isEmpty()) return ResponseEntity.notFound().build(); + var l = lockOpt.get(); + if (!l.getLockee().equals(myId)) return ResponseEntity.status(403).build(); + if (l.isTestLock()) return ResponseEntity.badRequest().build(); + if (l.getEmergencyUnlockRequestedAt() != null) return ResponseEntity.status(409).build(); + + l.setEmergencyUnlockRequestedAt(LocalDateTime.now()); + if (l.getKeyholder() == null) { + l.setEmergencyAutoUnlocked(true); + l.setKeyholderRequestedUnlock(true); + } else { + systemMessageService.send(myId, l.getKeyholder(), + "⚠️ NOTFALL: " + me.getName() + " bittet dringend um Freigabe des Locks. Bitte reagiere innerhalb einer Stunde.", + "/keyholder.html", de.oaa.xxx.social.entity.MessageCause.EMERGENCY); + } + timeLockRepository.save(l); + return ResponseEntity.noContent().build(); + } + + // ── Hilfsmethoden ───────────────────────────────────────────────────────────── + + private TimeLockEntity buildBaseEntity(TimeLockTemplateEntity template, UUID keyholder, UUID lockee, boolean testLock) { + TimeLockEntity lock = new TimeLockEntity(); + lock.setName(template.getName()); + lock.setLockee(lockee); + lock.setKeyholder(keyholder); + lock.setRequiresVerification(template.isRequiresVerification()); + lock.setTestLock(testLock); + lock.setEndTimeVisible(template.isEndTimeVisible()); + lock.setTasks(template.getTasks()); + lock.setTaskMode(template.getTaskCardMode()); + lock.setTaskEveryMinutes(template.getTaskEveryMinutes()); + lock.setMinTasksPerDay(template.getMinTasksPerDay()); + lock.setSpinningWheelEntries(template.getSpinningWheelEntries()); + lock.setSpinsEveryMinutes(template.getSpinsEveryMinutes()); + lock.setMinSpinsPerDay(template.getMinSpinsPerDay()); + lock.setHygineOpeningDurationMinutes(template.getHygineOpeningDurationMinutes()); + lock.setHygineOpeningEveryMinites(template.getHygineOpeningEveryMinites()); + lock.setPenaltyType(template.getPenaltyType()); + lock.setPenaltyValue(template.getPenaltyValue()); + lock.setMinTimeInMinutes(template.getMinTimeInMinutes()); + lock.setMaxTimeInMinutes(template.getMaxTimeInMinutes()); + return lock; + } + + + private byte[] scaleImage(byte[] input, int maxSize) throws IOException { + BufferedImage original = ImageIO.read(new ByteArrayInputStream(input)); + if (original == null) return input; + int w = original.getWidth(), h = original.getHeight(); + if (w <= maxSize && h <= maxSize) return input; + double scale = (double) maxSize / Math.max(w, h); + int newW = (int) (w * scale), newH = (int) (h * scale); + BufferedImage scaled = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_RGB); + Graphics2D g = scaled.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(original, 0, 0, newW, newH, null); + g.dispose(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(scaled, "jpeg", out); + return out.toByteArray(); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockEntity.java index 209135d..70762b2 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockEntity.java @@ -3,115 +3,60 @@ package de.oaa.xxx.games.chastity.timelock; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; -import java.util.UUID; +import de.oaa.xxx.games.chastity.common.BaseLockEntity; import de.oaa.xxx.games.chastity.common.PenaltyType; import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelConverter; import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry; -import de.oaa.xxx.games.chastity.tasks.Task; -import de.oaa.xxx.games.chastity.tasks.TaskListConverter; -import de.oaa.xxx.games.chastity.tasks.TaskMode; -import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -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 = "time_lock") -public class TimeLockEntity { +@DiscriminatorValue("TIMELOCK") +public class TimeLockEntity extends BaseLockEntity { - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - private UUID lockId; - @Column - private String name; - // Settings - @Column(nullable = false) - private UUID lockee; - @Column - private UUID keyholder; @Column private LocalDateTime startTime; @Column private LocalDateTime unlockTime; @Column private boolean endTimeVisible; - @Convert(converter = TaskListConverter.class) - @Column(columnDefinition = "TEXT") - private List tasks; + @Column + private Integer minTimeInMinutes; + @Column + private Integer maxTimeInMinutes; + @Column private Integer taskEveryMinutes; @Column private Integer minTasksPerDay; + @Convert(converter = SpinningWheelConverter.class) @Column(columnDefinition = "TEXT") private List spinningWheelEntries; - @Column - private Integer hygineOpeningDurationMinutes; - @Column - private Integer hygineOpeningEveryMinites; - @Column - private boolean requiresVerification; - @Column - private boolean testLock; - @Column - private Integer unlockCodeLength; - @Column(nullable = false) - private TaskMode taskMode = TaskMode.RANDOM; @Column private Integer spinsEveryMinutes; @Column private Integer minSpinsPerDay; + @Column private PenaltyType penaltyType; @Column private Integer penaltyValue; - - @Column - private LocalDateTime lastHygineOpening; - @Column - private LocalDateTime tempOpeningTime; // If null, not while hygine opening - @Column - private Integer tempOpeningDuration; - @Column - private TempOpeningReason tempOpeningReason; + @Column private LocalDateTime frozenFrom; - @Column - private LocalDateTime frozenUntil; - @Column - private String currentTask; - @Column(columnDefinition = "TEXT") - private String currentTaskDescription; - @Column - private LocalDateTime taskUntil; - @Column - private String unlockCode; - /** Keyholder hat Unlock angefordert – nächste Aktion der Lockee zeigt grüne Karte */ - @Column(nullable = false) - private boolean keyholderRequestedUnlock = false; - /** Lockee hat Notfall-Entsperrung angefordert */ - @Column - private java.time.LocalDateTime emergencyUnlockRequestedAt; - /** true = System hat automatisch entsperrt (Keyholderin nicht reagiert) */ - @Column(nullable = false) - private boolean emergencyAutoUnlocked = false; + @Column private List taskTimes; @Column private List spinningWheelTimes; @Column private LocalDate lastCheck; - - public TaskMode getTaskMode() { return taskMode != null ? taskMode : TaskMode.RANDOM; } - } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockRepository.java index 2b0954f..5d40b59 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockRepository.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockRepository.java @@ -6,4 +6,6 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface TimeLockRepository extends JpaRepository { + boolean existsByLockeeAndStartTimeIsNotNullAndUnlockTimeIsNull(UUID lockee); + } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockService.java index 5fe751d..ed5055f 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockService.java @@ -1,381 +1,372 @@ package de.oaa.xxx.games.chastity.timelock; -import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.Random; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.oaa.xxx.games.chastity.common.AbstractLockService; +import de.oaa.xxx.games.chastity.common.BaseLockEntity; +import de.oaa.xxx.games.chastity.common.BaseLockService; import de.oaa.xxx.games.chastity.common.CodeCreator; -import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationEntity; +import de.oaa.xxx.games.chastity.common.VerificationCommonDTO; +import de.oaa.xxx.games.chastity.community.CommunityPilloryEntity; +import de.oaa.xxx.games.chastity.community.CommunityPilloryReason; +import de.oaa.xxx.games.chastity.community.CommunityPilloryRepository; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationEntity; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationEntity; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationRepository; import de.oaa.xxx.games.chastity.lockcontroll.LockControl; import de.oaa.xxx.games.chastity.lockcontroll.LockControlCallback; import de.oaa.xxx.games.chastity.lockcontroll.TTLockControl; import de.oaa.xxx.games.chastity.lockcontroll.TrustLockControl; import de.oaa.xxx.games.chastity.lockcontroll.UnlockcodeLockControl; import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry; -import de.oaa.xxx.games.chastity.tasks.Task; import de.oaa.xxx.games.chastity.tasks.TaskMode; import de.oaa.xxx.games.chastity.unlock.TempOpeningReason; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; -import de.oaa.xxx.games.chastity.verification.VerificationRepository; -import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; -import de.oaa.xxx.games.history.GameHistoryEntity; import de.oaa.xxx.games.history.GameHistoryRepository; +import de.oaa.xxx.games.history.GameType; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; -public class TimeLockService extends AbstractLockService implements LockControlCallback { +public class TimeLockService extends BaseLockService implements LockControlCallback { - private static final Logger LOGGER = LoggerFactory.getLogger(TimeLockService.class); - private final TimeLockEntity lock; - private final TimeLockRepository timeLockRepository; - private final VerificationRepository verificationRepository; - private final GameHistoryRepository gameHistoryRepository; - private final UserRepository userRepository; - private final KeyholderNotificationRepository keyholderNotificationRepository; - private final UnlockCodeHistoryService unlockCodeHistoryService; - - private LockControl lockControl; + private static final Logger LOGGER = LoggerFactory.getLogger(TimeLockService.class); + private final TimeLockEntity lock; + private final TimeLockRepository timeLockRepository; + private final CommunityPilloryRepository pilloryRepository; - public TimeLockService(TimeLockEntity lock, VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, + private LockControl lockControl; + + public TimeLockService(TimeLockEntity lock, + CommunityVerificationRepository verificationRepository, + CommunityVerificationVoteRepository verificationVoteRepository, TimeLockRepository timeLockRepository, GameHistoryRepository gameHistoryRepository, - UserRepository userRepository, + UserRepository userRepository, KeyholderNotificationRepository keyholderNotificationRepository, - UnlockCodeHistoryService unlockCodeHistoryService) { - super(verificationVoteRepository); - this.lock = lock; - this.timeLockRepository = timeLockRepository; - this.verificationRepository = verificationRepository; - this.gameHistoryRepository = gameHistoryRepository; - this.userRepository = userRepository; - this.keyholderNotificationRepository = keyholderNotificationRepository; - this.unlockCodeHistoryService = unlockCodeHistoryService; - } - - public void init(TimeLockTemplate template, TimeLockAdditionalSettings settings) { - switch (settings.controllType()) { - case TTLOCK -> lockControl = new TTLockControl(); - case TRUST -> lockControl = new TrustLockControl(); - case UNLOCK_CODE -> lockControl = new UnlockcodeLockControl(this); - } - - lock.setLockee(UUID.randomUUID()); - lock.setName(template.name()); - lock.setLockee(settings.lockee()); - lock.setKeyholder(settings.keyholder()); - lock.setRequiresVerification(template.requiresVerification()); - lock.setTestLock(settings.testlock()); - lock.setUnlockCodeLength(settings.unlockCodeLength()); - - lock.setStartTime(LocalDateTime.now()); - Integer unlockTimeMinutes = template.maxTimeInMinutes(); - if (template.minTimeInMinutes() != null) { - unlockTimeMinutes = new Random().nextInt(template.minTimeInMinutes(), template.maxTimeInMinutes()); - } - lock.setUnlockTime(LocalDateTime.now().plusMinutes(unlockTimeMinutes)); - lock.setEndTimeVisible(template.endTimeVisible()); - - lock.setTasks(template.tasks()); - lock.setTaskEveryMinutes(template.taskEveryMinutes()); - lock.setMinTasksPerDay(template.minTasksPerDay()); - - lock.setSpinningWheelEntries(template.spinningWheelEntries()); - lock.setSpinsEveryMinutes(template.spinsEveryMinutes()); - lock.setMinSpinsPerDay(template.minSpinsPerDay()); - - lock.setHygineOpeningDurationMinutes(template.hygineOpeningDurationMinutes()); - lock.setHygineOpeningEveryMinites(template.hygineOpeningEveryMinites()); - - lock.setTaskMode(template.taskMode()); - - lockControl.lock(); - } - - public SpinningWheelEntry spinWheel() { - if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason() ) { - var entries = lock.getSpinningWheelEntries(); - var entry = entries.get(new Random().nextInt(entries.size())); - entry.getType().apply(this, entry.getIntVal(), entry.getStringVal()); - return entry; - } - // Nicht während der Hyhiene Öffnung - return null; - } + KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + KeyholderVerificationRepository keyholderVerificationRepository, + CommunityTaskVoteRepository communityTaskVoteRepository, + CommunityPilloryRepository pilloryRepository, + UnlockCodeHistoryService unlockCodeHistoryService, + SystemMessageService systemMessageService) { + super(verificationVoteRepository, verificationRepository, keyholderVerificationRepository, + gameHistoryRepository, userRepository, keyholderNotificationRepository, + systemMessageService, unlockCodeHistoryService, + keyholderTaskChoiceRepository, communityTaskVoteRepository); + this.lock = lock; + this.timeLockRepository = timeLockRepository; + this.pilloryRepository = pilloryRepository; + } - public void addTime(Integer intVal) { - LOGGER.debug("Lock addTime: %s minutes", intVal); - lock.setUnlockTime(lock.getUnlockTime().plusMinutes(intVal)); - } + // ── Abstract method implementations ────────────────────────────────────── - public void removeTime(Integer intVal) { - LOGGER.debug("Lock removeTime: %s minutes", intVal); - lock.setUnlockTime(lock.getUnlockTime().minusMinutes(intVal)); - } + @Override + protected BaseLockEntity getLock() { + return lock; + } - public void freeze(Integer intVal) { - LOGGER.debug("Lock frozen for %s minutes", intVal); - lock.setFrozenFrom(LocalDateTime.now()); - lock.setFrozenUntil(LocalDateTime.now().plusMinutes(intVal)); - } + @Override + protected void saveLock() { + timeLockRepository.save(lock); + } - public void freeze() { - LOGGER.debug("Lock frozen"); - lock.setFrozenFrom(LocalDateTime.now()); - } + @Override + protected GameType getGameType() { + return GameType.TIMELOCK; + } - public void unfreeze() { - if (lock.getFrozenFrom() != null) { - var unfreeTime = lock.getFrozenUntil() != null ? lock.getFrozenUntil() : LocalDateTime.now(); - var diff = ChronoUnit.MINUTES.between(lock.getFrozenFrom(), unfreeTime); - LOGGER.debug("Lock unfrozen - adding %s minutes to the lock", diff); - lock.setUnlockTime(lock.getUnlockTime().plusMinutes(diff)); - } else { - LOGGER.debug("Lock not frozen - ignore Call"); - } - } + @Override + protected void applyHygieneOvertime(Long overtime) { + lock.setUnlockTime(lock.getUnlockTime().plusMinutes(overtime * 4)); + } - public void task() { - if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason() ) { - switch (lock.getTaskMode()) { - case TaskMode.RANDOM -> applyRandomTask(); - case TaskMode.KEYHOLDER -> startKeyHolderVote(); - case TaskMode.COMMUNITY -> startCommunityVode(); - } - } - // Nicht während der Hyhiene Öffnung - } + // ── Hook overrides ──────────────────────────────────────────────────────── - private void startKeyHolderVote() { - // Keyholder Vote starten - } + @Override + protected void beforePhysicalUnlock() { + lockControl.unlock(); + } - private void startCommunityVode() { - // Community Vote starten - } + @Override + protected void afterHygieneClosing() { + lockControl.lock(); + } - public void applyRandomTask() { - LOGGER.debug("Apply random task"); - var tasks = lock.getTasks(); - if (!tasks.isEmpty()) { - task(tasks.get(new Random().nextInt(tasks.size()))); - } - } + // ── Initialisation ──────────────────────────────────────────────────────── - public void task(Task task) { - LOGGER.debug("Apply task {}", task); - lock.setCurrentTask(task.resolveTitle()); - lock.setCurrentTaskDescription(task.getDescription()); - if (task.getMinutes() != null && task.getMinutes() > 0) { - lock.setTaskUntil(LocalDateTime.now().plusMinutes(task.getMinutes())); - } - } + /** + * Initialisiert ein neues Lock anhand eines Template und Laufzeit-Einstellungen. + * Ruft am Ende lockControl.lock() auf – bei UNLOCK_CODE wird dabei der Entsperrcode + * generiert und das Lock bereits persistiert. + */ + public void init(TimeLockTemplateEntity template, TimeLockAdditionalSettings settings) { + switch (settings.controllType()) { + case TTLOCK -> lockControl = new TTLockControl(); + case TRUST -> lockControl = new TrustLockControl(); + case UNLOCK_CODE -> lockControl = new UnlockcodeLockControl(this); + } - public void text(Integer intVal, String stringVal) { - LOGGER.debug("Apply text {}", stringVal); - lock.setCurrentTask(stringVal); - if (intVal != null && intVal > 0) { - lock.setTaskUntil(LocalDateTime.now().plusMinutes(intVal)); - } - } + LocalDateTime now = LocalDateTime.now(); + lock.setStartTime(now); + lock.setName(template.getName()); + lock.setLockee(settings.lockee()); + lock.setKeyholder(settings.keyholder()); + lock.setRequiresVerification(template.isRequiresVerification()); + lock.setTestLock(settings.testlock()); + lock.setUnlockCodeLength(settings.unlockCodeLength() != null ? settings.unlockCodeLength() : 5); - public String clearTask() { - LOGGER.debug("Clear task"); - lock.setCurrentTask(null); - lock.setCurrentTaskDescription(null); - lock.setTaskUntil(null); - return ""; - } + Integer minMinutes = template.getMinTimeInMinutes(); + Integer maxMinutes = template.getMaxTimeInMinutes() != null ? template.getMaxTimeInMinutes() : 60; + int unlockTimeMinutes = (minMinutes != null && minMinutes < maxMinutes) + ? minMinutes + new Random().nextInt(maxMinutes - minMinutes) + : maxMinutes; + lock.setUnlockTime(now.plusMinutes(unlockTimeMinutes)); + lock.setEndTimeVisible(template.isEndTimeVisible()); - public void testUnfreeze() { - if (lock.getFrozenUntil().isAfter(LocalDateTime.now())) { - unfreeze(); - } - } + lock.setTasks(template.getTasks()); + lock.setTaskEveryMinutes(template.getTaskEveryMinutes()); + lock.setMinTasksPerDay(template.getMinTasksPerDay()); - public void unlock(String unlockCode) { - lockControl.unlock(); - this.lock.setUnlockTime(LocalDateTime.now()); - boolean valid = true; - if (lock.isEmergencyAutoUnlocked()) { - valid = false; - LOGGER.debug("Lock invalid - Emergency Auto-Unlock (1h timer)"); - } - if (lock.isTestLock()) { - valid = false; - } else if (Duration.between(lock.getStartTime(), lock.getUnlockTime()).toHours() > 24) { - Set verifications = verificationRepository.findByLockId(this.lock.getLockId()).stream() - .filter(verification -> isValid(verification)) - .map(verification -> verification.getVerificationTime().toLocalDate()).collect(Collectors.toSet()); + lock.setSpinningWheelEntries(template.getSpinningWheelEntries()); + lock.setSpinsEveryMinutes(template.getSpinsEveryMinutes()); + lock.setMinSpinsPerDay(template.getMinSpinsPerDay()); - LocalDate current = this.lock.getStartTime().toLocalDate(); - LocalDate last = this.lock.getUnlockTime().toLocalDate().minusDays(1); + lock.setHygineOpeningDurationMinutes(template.getHygineOpeningDurationMinutes()); + lock.setHygineOpeningEveryMinites(template.getHygineOpeningEveryMinites()); + if (template.getHygineOpeningEveryMinites() != null) { + lock.setLastHygineOpening(now); + } - 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.setTaskMode(template.getTaskCardMode()); + lock.setPenaltyType(template.getPenaltyType()); + lock.setPenaltyValue(template.getPenaltyValue()); + lock.setMinTimeInMinutes(template.getMinTimeInMinutes()); + lock.setMaxTimeInMinutes(template.getMaxTimeInMinutes()); - lock.setUnlockTime(LocalDateTime.now()); - LOGGER.debug("Unlocked at {}", lock.getUnlockTime()); - timeLockRepository.save(lock); + lockControl.lock(); + } - if (valid) { - long durationMinutes = Duration.between(lock.getStartTime(), lock.getUnlockTime()).toMinutes(); + // ── Spinning wheel ──────────────────────────────────────────────────────── - // 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) { - entry.addParticipant(lock.getKeyholder(), de.oaa.xxx.games.history.GameRole.KEYHOLDER); - } - gameHistoryRepository.save(entry); + public SpinningWheelEntry spinWheel() { + if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason()) { + var entries = lock.getSpinningWheelEntries(); + var entry = entries.get(new Random().nextInt(entries.size())); + entry.getType().apply(this, entry.getIntVal(), entry.getStringVal()); + return entry; + } + // Nicht während der Hygiene-Öffnung + return null; + } - int minutes = (int) durationMinutes; - userRepository.findById(lock.getLockee()).ifPresent(u -> { - u.setLockeeXp(u.getLockeeXp() + minutes); - userRepository.save(u); - }); - if (lock.getKeyholder() != null) { - userRepository.findById(lock.getKeyholder()).ifPresent(u -> { - u.setKeyholderXp(u.getKeyholderXp() + minutes); - userRepository.save(u); - }); - } - } - } + // ── Time controls ───────────────────────────────────────────────────────── - public void applyPenalty() { - if (lock.getPenaltyType() != null) { - switch (lock.getPenaltyType()) { - case ADD -> addTime(lock.getPenaltyValue()); - case FREEZE -> freeze(); - case PILLORY -> pillory(); - } - } - } + public void addTime(Integer intVal) { + LOGGER.debug("Lock addTime: %s minutes", intVal); + lock.setUnlockTime(lock.getUnlockTime().plusMinutes(intVal)); + } - public void check() { - LocalDate today = LocalDate.now(); - if (!lock.getStartTime().toLocalDate().equals(today)) { - if (lock.getLastCheck() != null || today.isAfter(lock.getLastCheck())) { - LOGGER.info("Check the day before for violations"); - LocalDate yesterday = today.minusDays(1); - boolean violation = false; - if (lock.getMinTasksPerDay() != null) { - if (lock.getMinTasksPerDay() > lock.getTaskTimes().stream().map(LocalDateTime::toLocalDate) - .filter(yesterday::equals).count()) { - violation = true; - } - } - if (lock.getMinSpinsPerDay() != null) { - if (lock.getMinSpinsPerDay() > lock.getSpinningWheelTimes().stream().map(LocalDateTime::toLocalDate) - .filter(yesterday::equals).count()) { - violation = true; - } - } - if (violation) { - applyPenalty(); - } - lock.setLastCheck(today); - timeLockRepository.save(lock); - } - } - } + public void removeTime(Integer intVal) { + LOGGER.debug("Lock removeTime: %s minutes", intVal); + lock.setUnlockTime(lock.getUnlockTime().minusMinutes(intVal)); + } - public void pillory() { - // TODO an den Pranger stellen - } + public void freeze(Integer intVal) { + LOGGER.debug("Lock frozen for %s minutes", intVal); + lock.setFrozenFrom(LocalDateTime.now()); + lock.setFrozenUntil(LocalDateTime.now().plusMinutes(intVal)); + } - @Override - public void setUnlockCode(String code) { - lock.setUnlockCode(code); - timeLockRepository.save(lock); - } + public void freeze() { + LOGGER.debug("Lock frozen"); + lock.setFrozenFrom(LocalDateTime.now()); + } - @Override - public int getUnlockcodeLenght() { - return lock.getUnlockCodeLength(); - } - - - public void startHygieneOpening() { - tempOperning(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes()); - } - - private Long calcOvertime() { - LocalDateTime now = LocalDateTime.now(); - Long overtime = null; - if (lock.getTempOpeningTime() != null && lock.getTempOpeningDuration() != null) { - LocalDateTime dueTime = lock.getTempOpeningTime().plusMinutes(lock.getTempOpeningDuration()); - if (LocalDateTime.now().isAfter(dueTime)) { - overtime = ChronoUnit.MINUTES.between(dueTime, now); - } - } - return overtime; - } - - public String endHygieneOpening() { - lockControl.lock(); - LocalDateTime now = LocalDateTime.now(); + public void unfreeze() { + if (lock.getFrozenFrom() != null) { + var unfreeTime = lock.getFrozenUntil() != null ? lock.getFrozenUntil() : LocalDateTime.now(); + var diff = ChronoUnit.MINUTES.between(lock.getFrozenFrom(), unfreeTime); + LOGGER.debug("Lock unfrozen - adding %s minutes to the lock", diff); + lock.setUnlockTime(lock.getUnlockTime().plusMinutes(diff)); + } else { + LOGGER.debug("Lock not frozen - ignore Call"); + } + } - Long overtime = calcOvertime(); - if (overtime != null) { - if (lock.getKeyholder() != null) { - reportKeyholder(overtime); - } - addOvertime(overtime); - } - lock.setLastHygineOpening(now); - lock.setTempOpeningDuration(null); - lock.setTempOpeningTime(null); - - var code = CodeCreator.createAlphanumericCode(lock.getUnlockCodeLength()); - lock.setUnlockCode(code); - timeLockRepository.save(lock); - return code; - } + public void testUnfreeze() { + if (lock.getFrozenUntil().isAfter(LocalDateTime.now())) { + unfreeze(); + } + } - private void reportKeyholder(Long overtime) { - KeyholderNotificationEntity notification = new KeyholderNotificationEntity(); - notification.setLockId(lock.getLockId()); - notification.setLockeeId(lock.getLockee()); - notification.setKeyholderUserId(lock.getKeyholder()); - notification.setViolationTime(LocalDateTime.now()); - notification.setOvertimeMinutes(overtime); - keyholderNotificationRepository.save(notification); - } + // ── Tasks ───────────────────────────────────────────────────────────────── - private void addOvertime(Long overtime) { - lock.setUnlockTime(lock.getUnlockTime().plusMinutes(overtime * 4)); - } - - private void tempOperning(TempOpeningReason reason, Integer duration) { - assert duration != null; - lockControl.unlock(); - lock.setTempOpeningReason(reason); - lock.setTempOpeningTime(LocalDateTime.now());; - lock.setTempOpeningDuration(duration); - timeLockRepository.save(lock); - unlockCodeHistoryService.save(lock.getLockee(), lock.getLockId(), lock.getName(), lock.getUnlockCode(), reason.toString()); - } + public void task() { + if (TempOpeningReason.HYGIENE != lock.getTempOpeningReason()) { + switch (lock.getTaskMode()) { + case TaskMode.RANDOM -> applyRandomTask(); + case TaskMode.KEYHOLDER -> startKeyholderVote(); + case TaskMode.COMMUNITY -> { + if (lock.isTestLock()) applyRandomTask(); + else startCommunityVote(); + } + } + } + // Nicht während der Hygiene-Öffnung + } + + public void text(Integer intVal, String stringVal) { + LOGGER.debug("Apply text {}", stringVal); + lock.setCurrentTask(stringVal); + if (intVal != null && intVal > 0) { + lock.setTaskUntil(LocalDateTime.now().plusMinutes(intVal)); + } + } + + // ── Verification ────────────────────────────────────────────────────────── + + /** + * Gibt eine bestehende Verifikation für heute zurück (Idempotenz) oder legt eine neue an. + * Erstellt je nach Keyholder-Präsenz eine KeyholderVerification oder CommunityVerification. + */ + public VerificationCommonDTO startVerification() { + LocalDate today = LocalDate.now(); + LocalDateTime todayStart = today.atStartOfDay(); + LocalDateTime todayEnd = todayStart.plusDays(1); + + if (lock.getKeyholder() != null) { + var existing = keyholderVerificationRepository.findByLockId(lock.getLockId()).stream() + .filter(v -> v.getCreatedAt().toLocalDate().equals(today)) + .findFirst(); + if (existing.isPresent()) return existing.get().toCommonVerification(); + + KeyholderVerificationEntity v = new KeyholderVerificationEntity(); + v.setId(UUID.randomUUID()); + v.setLockId(lock.getLockId()); + v.setLockeeId(lock.getLockee()); + v.setKeyholderId(lock.getKeyholder()); + v.setCode(CodeCreator.createAlphanumeric(6)); + v.setCreatedAt(LocalDateTime.now()); + keyholderVerificationRepository.save(v); + return v.toCommonVerification(); + } else { + var pending = communityVerificationRepository + .findByLockIdAndCreatedAtBetweenAndImageIsNull(lock.getLockId(), todayStart, todayEnd); + if (!pending.isEmpty()) return pending.get(0).toCommonVerification(); + var completed = communityVerificationRepository + .findByLockIdAndCreatedAtBetweenAndImageIsNotNull(lock.getLockId(), todayStart, todayEnd); + if (!completed.isEmpty()) return completed.get(0).toCommonVerification(); + + CommunityVerificationEntity v = new CommunityVerificationEntity(); + v.setId(UUID.randomUUID()); + v.setLockId(lock.getLockId()); + v.setLockeeId(lock.getLockee()); + v.setCode(CodeCreator.createAlphanumeric(6)); + v.setCreatedAt(LocalDateTime.now()); + communityVerificationRepository.save(v); + return v.toCommonVerification(); + } + } + + /** + * Speichert das Verifikationsbild auf der richtigen Entity (Keyholder oder Community). + * Gibt false zurück wenn die Verifikation nicht gefunden wurde oder nicht zu diesem Lock gehört. + */ + public boolean completeVerification(UUID verificationId, byte[] image) { + if (lock.getKeyholder() != null) { + var vOpt = keyholderVerificationRepository.findById(verificationId); + if (vOpt.isEmpty() || !vOpt.get().getLockId().equals(lock.getLockId())) return false; + var v = vOpt.get(); + v.setImage(image); + keyholderVerificationRepository.save(v); + } else { + var vOpt = communityVerificationRepository.findById(verificationId); + if (vOpt.isEmpty() || !vOpt.get().getLockId().equals(lock.getLockId())) return false; + var v = vOpt.get(); + v.setImage(image); + communityVerificationRepository.save(v); + } + return true; + } + + // ── Penalty & check ─────────────────────────────────────────────────────── + + public void applyPenalty() { + if (lock.getPenaltyType() != null) { + switch (lock.getPenaltyType()) { + case ADD -> addTime(lock.getPenaltyValue()); + case FREEZE -> freeze(); + case PILLORY -> pillory(CommunityPilloryReason.HYGIENE_OPENING_EXEEDED, null); + } + } + } + + public void check() { + LocalDate today = LocalDate.now(); + if (!lock.getStartTime().toLocalDate().equals(today)) { + if (lock.getLastCheck() != null || today.isAfter(lock.getLastCheck())) { + LOGGER.info("Check the day before for violations"); + LocalDate yesterday = today.minusDays(1); + boolean violation = false; + if (lock.getMinTasksPerDay() != null) { + if (lock.getMinTasksPerDay() > lock.getTaskTimes().stream().map(LocalDateTime::toLocalDate) + .filter(yesterday::equals).count()) { + violation = true; + } + } + if (lock.getMinSpinsPerDay() != null) { + if (lock.getMinSpinsPerDay() > lock.getSpinningWheelTimes().stream() + .map(LocalDateTime::toLocalDate).filter(yesterday::equals).count()) { + violation = true; + } + } + if (violation) { + applyPenalty(); + } + lock.setLastCheck(today); + timeLockRepository.save(lock); + } + } + } + + public void pillory(CommunityPilloryReason reason, UUID keyholderId) { + CommunityPilloryEntity pillory = new CommunityPilloryEntity(); + pillory.setCreatedAt(LocalDateTime.now()); + pillory.setLockeeId(lock.getLockee()); + pillory.setLockId(lock.getLockId()); + pillory.setReason(reason); + pillory.setKeyholderId(keyholderId); + pilloryRepository.save(pillory); + } + + // ── Hygiene opening ─────────────────────────────────────────────────────── + + public void startHygieneOpening() { + lockControl.unlock(); + startTempOpening(TempOpeningReason.HYGIENE, lock.getHygineOpeningDurationMinutes()); + } + + // ── LockControlCallback ─────────────────────────────────────────────────── + + @Override + public void setUnlockCode(String code) { + lock.setUnlockCode(code); + timeLockRepository.save(lock); + } + + @Override + public int getUnlockcodeLenght() { + return lock.getUnlockCodeLength(); + } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockServiceFactory.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockServiceFactory.java index a5db147..f8f7e9f 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockServiceFactory.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockServiceFactory.java @@ -2,53 +2,64 @@ package de.oaa.xxx.games.chastity.timelock; import org.springframework.stereotype.Service; +import de.oaa.xxx.games.chastity.community.CommunityPilloryRepository; +import de.oaa.xxx.games.chastity.community.CommunityTaskVoteRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationRepository; +import de.oaa.xxx.games.chastity.community.CommunityVerificationVoteRepository; import de.oaa.xxx.games.chastity.keyholder.KeyholderNotificationRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderTaskChoiceRepository; +import de.oaa.xxx.games.chastity.keyholder.KeyholderVerificationRepository; import de.oaa.xxx.games.chastity.unlock.UnlockCodeHistoryService; -import de.oaa.xxx.games.chastity.verification.VerificationRepository; -import de.oaa.xxx.games.chastity.verification.VerificationVoteRepository; import de.oaa.xxx.games.history.GameHistoryRepository; +import de.oaa.xxx.social.SystemMessageService; import de.oaa.xxx.user.UserRepository; + @Service public class TimeLockServiceFactory { + private final TimeLockRepository timeLockRepository; + private final CommunityVerificationRepository communityVerificationRepository; + private final GameHistoryRepository gameHistoryRepository; + private final UserRepository userRepository; + private final KeyholderNotificationRepository keyholderNotificationRepository; + private final CommunityPilloryRepository pilloryRepository; + private final CommunityTaskVoteRepository communityTaskVoteRepository; + private final KeyholderTaskChoiceRepository keyholderTaskChoiceRepository; + private final KeyholderVerificationRepository keyholderVerificationRepository; - private final VerificationRepository verificationRepository; - private final VerificationVoteRepository verificationVoteRepository; - private final TimeLockRepository timeLockRepository; - private final GameHistoryRepository gameHistoryRepository; - private final UserRepository userRepository; - private KeyholderNotificationRepository keyholderNotificationRepository; - private final UnlockCodeHistoryService unlockCodeHistoryService; + private final UnlockCodeHistoryService unlockCodeHistoryService; + private final SystemMessageService systemMessageService; + private CommunityVerificationVoteRepository communityVerificationVoteRepository; - public TimeLockServiceFactory(VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, - TimeLockRepository timeLockRepository, - GameHistoryRepository gameHistoryRepository, - UserRepository userRepository, - KeyholderNotificationRepository keyholderNotificationRepository, - UnlockCodeHistoryService unlockCodeHistoryService) { - this.verificationRepository = verificationRepository; - this.verificationVoteRepository = verificationVoteRepository; - this.timeLockRepository = timeLockRepository; - this.gameHistoryRepository = gameHistoryRepository; - this.userRepository = userRepository; + public TimeLockServiceFactory(CommunityVerificationRepository verificationRepository, + CommunityVerificationVoteRepository verificationVoteRepository, TimeLockRepository timeLockRepository, + GameHistoryRepository gameHistoryRepository, UserRepository userRepository, + KeyholderNotificationRepository keyholderNotificationRepository, + KeyholderTaskChoiceRepository keyholderTaskChoiceRepository, + KeyholderVerificationRepository keyholderVerificationRepository, + CommunityTaskVoteRepository communityTaskVoteRepository, CommunityPilloryRepository pilloryRepository, + UnlockCodeHistoryService unlockCodeHistoryService, SystemMessageService systemMessageService) { + this.communityVerificationVoteRepository = verificationVoteRepository; + this.timeLockRepository = timeLockRepository; + this.communityVerificationRepository = verificationRepository; + this.gameHistoryRepository = gameHistoryRepository; + this.userRepository = userRepository; this.keyholderNotificationRepository = keyholderNotificationRepository; - this.unlockCodeHistoryService = unlockCodeHistoryService; - } + this.pilloryRepository = pilloryRepository; + this.unlockCodeHistoryService = unlockCodeHistoryService; + this.systemMessageService = systemMessageService; + this.keyholderTaskChoiceRepository = keyholderTaskChoiceRepository; + this.communityTaskVoteRepository = communityTaskVoteRepository; + this.keyholderVerificationRepository = keyholderVerificationRepository; + } - /** - * Erstellt eine neue CardLockService-Instanz für das gegebene Lock. - */ - public TimeLockService create(TimeLockEntity lock) { - return new TimeLockService( - lock, - verificationRepository, - verificationVoteRepository, - timeLockRepository, - gameHistoryRepository, - userRepository, - keyholderNotificationRepository, - unlockCodeHistoryService - ); - } + /** + * Erstellt eine neue CardLockService-Instanz für das gegebene Lock. + */ + public TimeLockService create(TimeLockEntity lock) { + return new TimeLockService(lock, communityVerificationRepository, communityVerificationVoteRepository, + timeLockRepository, gameHistoryRepository, userRepository, keyholderNotificationRepository, + keyholderTaskChoiceRepository, keyholderVerificationRepository, communityTaskVoteRepository, + pilloryRepository, unlockCodeHistoryService, systemMessageService); + } } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateController.java new file mode 100644 index 0000000..72319ea --- /dev/null +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateController.java @@ -0,0 +1,148 @@ +package de.oaa.xxx.games.chastity.timelock; + +import de.oaa.xxx.games.chastity.common.PenaltyType; +import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry; +import de.oaa.xxx.games.chastity.tasks.Task; +import de.oaa.xxx.games.chastity.tasks.TaskMode; +import de.oaa.xxx.user.UserRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; +import java.util.*; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/timelock/templates") +public class TimeLockTemplateController { + + private final TimeLockTemplateRepository templateRepository; + private final UserRepository userRepository; + + public TimeLockTemplateController(TimeLockTemplateRepository templateRepository, + UserRepository userRepository) { + this.templateRepository = templateRepository; + this.userRepository = userRepository; + } + + record TemplateRequest( + String name, + Integer minTimeInMinutes, + Integer maxTimeInMinutes, + boolean endTimeVisible, + Integer hygineOpeningDurationMinutes, + Integer hygineOpeningEveryMinites, + List tasks, + Integer taskEveryMinutes, + Integer minTasksPerDay, + List spinningWheelEntries, + Integer spinsEveryMinutes, + Integer minSpinsPerDay, + boolean requiresVerification, + TaskMode taskMode, + PenaltyType penaltyType, + Integer penaltyValue + ) {} + + private Map toDto(TimeLockTemplateEntity t) { + Map dto = new LinkedHashMap<>(); + dto.put("templateId", t.getTemplateId()); + dto.put("name", t.getName()); + dto.put("minTimeInMinutes", t.getMinTimeInMinutes()); + dto.put("maxTimeInMinutes", t.getMaxTimeInMinutes()); + dto.put("endTimeVisible", t.isEndTimeVisible()); + dto.put("hygineOpeningEveryMinites", t.getHygineOpeningEveryMinites()); + dto.put("hygineOpeningDurationMinutes", t.getHygineOpeningDurationMinutes()); + dto.put("tasks", t.getTasks() != null ? t.getTasks() : List.of()); + dto.put("taskEveryMinutes", t.getTaskEveryMinutes()); + dto.put("minTasksPerDay", t.getMinTasksPerDay()); + dto.put("spinningWheelEntries", t.getSpinningWheelEntries() != null ? t.getSpinningWheelEntries() : List.of()); + dto.put("spinsEveryMinutes", t.getSpinsEveryMinutes()); + dto.put("minSpinsPerDay", t.getMinSpinsPerDay()); + dto.put("requiresVerification", t.isRequiresVerification()); + dto.put("taskMode", t.getTaskCardMode()); + dto.put("penaltyType", t.getPenaltyType()); + dto.put("penaltyValue", t.getPenaltyValue()); + return dto; + } + + @GetMapping + public ResponseEntity>> list(Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + List> result = templateRepository.findByOwner(myId) + .stream().map(this::toDto).collect(Collectors.toList()); + return ResponseEntity.ok(result); + } + + @PostMapping + public ResponseEntity> create(@RequestBody TemplateRequest req, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + if (req.name() == null || req.name().isBlank()) return ResponseEntity.badRequest().build(); + if (req.maxTimeInMinutes() == null || req.maxTimeInMinutes() < 1) return ResponseEntity.badRequest().build(); + + TimeLockTemplateEntity t = new TimeLockTemplateEntity(); + t.setOwner(myId); + applyRequest(t, req); + templateRepository.save(t); + return ResponseEntity.ok(toDto(t)); + } + + @PutMapping("/{id}") + public ResponseEntity> update(@PathVariable UUID id, + @RequestBody TemplateRequest req, + Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var opt = templateRepository.findById(id); + if (opt.isEmpty()) return ResponseEntity.notFound().build(); + TimeLockTemplateEntity t = opt.get(); + if (!t.getOwner().equals(myId)) return ResponseEntity.status(403).build(); + + if (req.name() == null || req.name().isBlank()) return ResponseEntity.badRequest().build(); + if (req.maxTimeInMinutes() == null || req.maxTimeInMinutes() < 1) return ResponseEntity.badRequest().build(); + + applyRequest(t, req); + templateRepository.save(t); + return ResponseEntity.ok(toDto(t)); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable UUID id, Principal principal) { + var meOpt = userRepository.findByEmail(principal.getName()); + if (meOpt.isEmpty()) return ResponseEntity.status(401).build(); + UUID myId = meOpt.get().getUserId(); + + var opt = templateRepository.findById(id); + if (opt.isEmpty()) return ResponseEntity.notFound().build(); + if (!opt.get().getOwner().equals(myId)) return ResponseEntity.status(403).build(); + + templateRepository.deleteById(id); + return ResponseEntity.noContent().build(); + } + + private void applyRequest(TimeLockTemplateEntity t, TemplateRequest req) { + t.setName(req.name()); + t.setMinTimeInMinutes(req.minTimeInMinutes()); + t.setMaxTimeInMinutes(req.maxTimeInMinutes()); + t.setEndTimeVisible(req.endTimeVisible()); + t.setHygineOpeningEveryMinites(req.hygineOpeningEveryMinites()); + t.setHygineOpeningDurationMinutes(req.hygineOpeningDurationMinutes()); + t.setTasks(req.tasks() != null ? req.tasks() : List.of()); + t.setTaskEveryMinutes(req.taskEveryMinutes()); + t.setMinTasksPerDay(req.minTasksPerDay()); + t.setSpinningWheelEntries(req.spinningWheelEntries() != null ? req.spinningWheelEntries() : List.of()); + t.setSpinsEveryMinutes(req.spinsEveryMinutes()); + t.setMinSpinsPerDay(req.minSpinsPerDay()); + t.setRequiresVerification(req.requiresVerification()); + t.setTaskMode(req.taskMode() != null ? req.taskMode() : TaskMode.RANDOM); + t.setPenaltyType(req.penaltyType()); + t.setPenaltyValue(req.penaltyValue()); + } +} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateEntity.java index 52d1d55..db6e363 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateEntity.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateEntity.java @@ -1,38 +1,25 @@ package de.oaa.xxx.games.chastity.timelock; import java.util.List; -import java.util.UUID; +import de.oaa.xxx.games.chastity.common.BaseLockTemplateEntity; import de.oaa.xxx.games.chastity.common.PenaltyType; import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelConverter; import de.oaa.xxx.games.chastity.spinningwheel.SpinningWheelEntry; -import de.oaa.xxx.games.chastity.tasks.Task; -import de.oaa.xxx.games.chastity.tasks.TaskListConverter; import de.oaa.xxx.games.chastity.tasks.TaskMode; import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; -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 = "timelock_template") -public class TimeLockTemplateEntity { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - @Column - private UUID templateId; - @Column(nullable = false) - private UUID owner; - @Column - private String name; +@DiscriminatorValue("TIMELOCK") +public class TimeLockTemplateEntity extends BaseLockTemplateEntity { + @Column private Integer minTimeInMinutes; @Column @@ -40,18 +27,9 @@ public class TimeLockTemplateEntity { @Column private boolean endTimeVisible; @Column - private Integer hygineOpeningDurationMinutes; - @Column - private Integer hygineOpeningEveryMinites; - - @Convert(converter = TaskListConverter.class) - @Column(columnDefinition = "TEXT") - private List tasks; - @Column private Integer taskEveryMinutes; @Column private Integer minTasksPerDay; - @Convert(converter = SpinningWheelConverter.class) @Column(columnDefinition = "TEXT") private List spinningWheelEntries; @@ -59,24 +37,15 @@ public class TimeLockTemplateEntity { private Integer spinsEveryMinutes; @Column private Integer minSpinsPerDay; - - @Column - private boolean requiresVerification; - @Column(nullable = false) - private TaskMode taskMode = TaskMode.RANDOM; @Column private PenaltyType penaltyType; @Column private Integer penaltyValue; - public TaskMode getTaskCardMode() { - return taskMode != null ? taskMode : TaskMode.RANDOM; - } - public TimeLockTemplate toTimeLockTemplate() { - return new TimeLockTemplate(templateId, owner, name, minTimeInMinutes, maxTimeInMinutes, endTimeVisible, - hygineOpeningDurationMinutes, hygineOpeningEveryMinites, tasks, taskEveryMinutes, minTasksPerDay, - spinningWheelEntries, spinsEveryMinutes, minSpinsPerDay, requiresVerification, taskMode, penaltyType, + return new TimeLockTemplate(getTemplateId(), getOwner(), getName(), minTimeInMinutes, maxTimeInMinutes, endTimeVisible, + getHygineOpeningDurationMinutes(), getHygineOpeningEveryMinites(), getTasks(), taskEveryMinutes, minTasksPerDay, + spinningWheelEntries, spinsEveryMinutes, minSpinsPerDay, isRequiresVerification(), getTaskMode(), penaltyType, penaltyValue); } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimelockTemplateRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateRepository.java similarity index 79% rename from xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimelockTemplateRepository.java rename to xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateRepository.java index b9efbbf..3dc9625 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimelockTemplateRepository.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateRepository.java @@ -5,6 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; import java.util.UUID; -public interface TimelockTemplateRepository extends JpaRepository { +public interface TimeLockTemplateRepository extends JpaRepository { List findByOwner(UUID owner); } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateService.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateService.java index 49d95f6..d12ccd6 100644 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateService.java +++ b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/timelock/TimeLockTemplateService.java @@ -13,9 +13,9 @@ import de.oaa.xxx.util.ValidationResult; public class TimeLockTemplateService { - private TimelockTemplateRepository timelockTemplateRepository; + private TimeLockTemplateRepository timelockTemplateRepository; - public TimeLockTemplateService(TimelockTemplateRepository timelockTemplateRepository) { + public TimeLockTemplateService(TimeLockTemplateRepository timelockTemplateRepository) { this.timelockTemplateRepository = timelockTemplateRepository; } diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationController.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationController.java deleted file mode 100644 index 73793c1..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationController.java +++ /dev/null @@ -1,158 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.security.Principal; -import java.time.LocalDateTime; -import java.util.Base64; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -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 de.oaa.xxx.games.chastity.common.CodeCreator; -import de.oaa.xxx.user.UserRepository; - -@RestController -@RequestMapping("/verification") -@Transactional -public class VerificationController { - - - - private final VerificationRepository verificationRepository; - private final VerificationVoteRepository verificationVoteRepository; - private final UserRepository userRepository; - - public VerificationController(VerificationRepository verificationRepository, - VerificationVoteRepository verificationVoteRepository, UserRepository userRepository) { - this.verificationRepository = verificationRepository; - this.verificationVoteRepository = verificationVoteRepository; - this.userRepository = userRepository; - } - - @GetMapping("/{verificationId}") - public ResponseEntity get(@PathVariable UUID verificationId) { - var optional = verificationRepository.findById(verificationId); - if (optional.isEmpty()) { - return ResponseEntity.noContent().build(); - } - var dto = optional.get().toVerification(); - verificationVoteRepository.findAllByVerificationId(verificationId).stream() - .map(VerificationVoteEntity::toVerificationVote) - .forEach(dto.votes()::add); - return ResponseEntity.ok(dto); - } - - @GetMapping("/") - public ResponseEntity> getAll( - @RequestParam(defaultValue = "1") int page, - @RequestParam(defaultValue = "10") int size) { - var paging = PageRequest.of(page, size, Sort.by("verificationTime").descending()); - Page result = verificationRepository.findAllByImageIsNotNull(paging); - return ResponseEntity.ok(result.stream().map(VerificationEntity::toVerification).toList()); - } - - @GetMapping("/new") - public ResponseEntity createVerification() { - var verification = new VerificationEntity(); - verification.setVerficationId(UUID.randomUUID()); - verification.setCode(CodeCreator.createAlphanumericCode(6)); - verification.setVerificationTime(LocalDateTime.now()); - verificationRepository.save(verification); - return ResponseEntity.ok(verification.toVerification()); - } - - @PutMapping("/{verificationId}") - public ResponseEntity update(@PathVariable UUID verificationId, @RequestBody VerificationDTO dto, - Principal principal) { - var user = userRepository.findByEmail(principal.getName()).orElse(null); - if (user == null) { - return ResponseEntity.status(401).build(); - } - var entity = verificationRepository.findById(verificationId).orElse(null); - if (entity == null) { - return ResponseEntity.notFound().build(); - } - if (entity.getVerificationTime().isBefore(LocalDateTime.now().minusHours(1))) { - return ResponseEntity.status(HttpStatus.GONE).build(); - } - if (dto.image() != null) { - entity.setImage(dto.image()); - } - verificationRepository.save(entity); - return ResponseEntity.ok().build(); - } - - @GetMapping("/community") - public ResponseEntity>> getCommunity( - @RequestParam(defaultValue = "0") int page, - Principal principal) { - var user = userRepository.findByEmail(principal.getName()).orElse(null); - if (user == null) return ResponseEntity.status(401).build(); - UUID myId = user.getUserId(); - - LocalDateTime since = LocalDateTime.now().minusHours(24); - LocalDateTime until = LocalDateTime.now(); - var paging = PageRequest.of(page, 10, Sort.by("verificationTime").descending()); - Page result = verificationRepository - .findByKeyholderIsNullAndVerificationTimeBetweenAndImageIsNotNull(since, until, paging); - - List> items = result.getContent().stream().map(v -> { - var votes = verificationVoteRepository.findAllByVerificationId(v.getVerficationId()); - long upvotes = votes.stream().filter(VerificationVoteEntity::isUpvote).count(); - long downvotes = votes.stream().filter(vt -> !vt.isUpvote()).count(); - var myVoteOpt = votes.stream().filter(vt -> myId.equals(vt.getUserId())).findFirst(); - boolean isOwn = myId.equals(v.getLockeeId()); - - Map item = new HashMap<>(); - item.put("verificationId", v.getVerficationId().toString()); - item.put("verificationTime", v.getVerificationTime().toString()); - item.put("code", v.getCode()); - item.put("image", v.getImage() != null ? Base64.getEncoder().encodeToString(v.getImage()) : null); - item.put("upvotes", upvotes); - item.put("downvotes", downvotes); - item.put("myVote", isOwn ? "own" : myVoteOpt.map(VerificationVoteEntity::isUpvote).orElse(null)); - item.put("hasMore", result.hasNext()); - return item; - }).toList(); - - return ResponseEntity.ok(items); - } - - @PostMapping("/{verificationId}/vote/") - public ResponseEntity addVote(@PathVariable UUID verificationId, @RequestBody VerificationVoteDTO dto, - Principal principal) { - var user = userRepository.findByEmail(principal.getName()).orElse(null); - if (user == null) return ResponseEntity.status(401).build(); - if (!verificationRepository.existsById(verificationId)) return ResponseEntity.notFound().build(); - - var vEntity = verificationRepository.findById(verificationId).orElse(null); - if (vEntity == null) return ResponseEntity.notFound().build(); - if (user.getUserId().equals(vEntity.getLockeeId())) return ResponseEntity.status(403).build(); - if (verificationVoteRepository.findByVerificationIdAndUserId(verificationId, user.getUserId()).isPresent()) { - return ResponseEntity.status(409).build(); - } - var vote = new VerificationVoteEntity(); - vote.setVoteId(UUID.randomUUID()); - vote.setVerificationId(verificationId); - vote.setUserId(user.getUserId()); - vote.setUpvote(dto.upvote()); - verificationVoteRepository.save(vote); - return ResponseEntity.accepted().build(); - } - -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationDTO.java deleted file mode 100644 index 1b9491c..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationDTO.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; - -public record VerificationDTO(UUID verficationId, String code, LocalDateTime verificationTime, byte[] image, List votes) {} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationEntity.java deleted file mode 100644 index 2fb93cd..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationEntity.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.UUID; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -@Table(name = "verification") -public class VerificationEntity { - - @Id - @Column - private UUID verficationId; - @Column(nullable = false) - private UUID lockId; - @Column(nullable = false) - private String code; - @Column(nullable = false) - private LocalDateTime verificationTime; - @Column(columnDefinition = "MEDIUMBLOB") - private byte[] image; - @Column - private UUID lockeeId; - @Column - private UUID keyholder; - - public UUID getKeyholderId() { - return keyholder; - } - - public void setKeyholderId(UUID keyholder) { - this.keyholder = keyholder; - } - - public VerificationDTO toVerification() { - return new VerificationDTO(verficationId, code, verificationTime, image, new ArrayList<>()); - } -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationRepository.java deleted file mode 100644 index 2549d06..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.util.UUID; - -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface VerificationRepository extends JpaRepository { - - org.springframework.data.domain.Page findAllByImageIsNotNull(Pageable pageable); - - java.util.List findByLockId(UUID lockId); - - java.util.List findByLockIdAndVerificationTimeBetweenAndImageIsNotNull(UUID lockId, java.time.LocalDateTime from, java.time.LocalDateTime to); - - java.util.List findByLockIdAndVerificationTimeBetweenAndImageIsNull(UUID lockId, java.time.LocalDateTime from, java.time.LocalDateTime to); - - org.springframework.data.domain.Page findByKeyholderIsNullAndVerificationTimeBetweenAndImageIsNotNull( - java.time.LocalDateTime from, java.time.LocalDateTime to, org.springframework.data.domain.Pageable pageable); -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteDTO.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteDTO.java deleted file mode 100644 index cd3979a..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteDTO.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.util.UUID; - -public record VerificationVoteDTO (UUID voteId, UUID userId, boolean upvote) {} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteRepository.java deleted file mode 100644 index b2bec8f..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/verification/VerificationVoteRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.oaa.xxx.games.chastity.verification; - -import java.util.List; -import java.util.UUID; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface VerificationVoteRepository extends JpaRepository { - - List findAllByVerificationId(UUID verificationId); - - java.util.Optional findByVerificationIdAndUserId(UUID verificationId, UUID userId); - - void deleteAllByVerificationId(UUID verificationId); -} - - diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntity.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntity.java deleted file mode 100644 index 758575f..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntity.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.oaa.xxx.games.chastity.vote; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; - -import java.time.LocalDateTime; -import java.util.UUID; - -@Getter -@Setter -@Entity -@Table(name = "community_task_vote") -public class CommunityTaskVoteEntity { - - @Id - @GeneratedValue(strategy = GenerationType.UUID) - private UUID voteSessionId; - - @Column(nullable = false) - private UUID lockId; - - /** ACTIVE | COMPLETED */ - @Column(nullable = false) - private String status = "ACTIVE"; - - @Column(nullable = false) - private LocalDateTime createdAt; - - @Column(nullable = false) - private LocalDateTime expiresAt; - - /** true = TestLock, nicht der Community zeigen */ - @Column(nullable = false) - private boolean testLock = false; - - /** null until completed */ - @Column - private Integer winningTaskIndex; -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryRepository.java deleted file mode 100644 index 1c1f7f9..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteEntryRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.oaa.xxx.games.chastity.vote; - -import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; -import java.util.UUID; - -public interface CommunityTaskVoteEntryRepository extends JpaRepository { - List findByVoteSessionId(UUID voteSessionId); - boolean existsByVoteSessionIdAndVoterUserId(UUID voteSessionId, UUID voterUserId); - Integer countByVoteSessionIdAndTaskIndex(UUID voteSessionId, int taskIndex); -} diff --git a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteRepository.java b/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteRepository.java deleted file mode 100644 index f03d316..0000000 --- a/xxxthegame/src/main/java/de/oaa/xxx/games/chastity/vote/CommunityTaskVoteRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.oaa.xxx.games.chastity.vote; - -import org.springframework.data.jpa.repository.JpaRepository; -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; - -public interface CommunityTaskVoteRepository extends JpaRepository { - List findByStatus(String status); - List findByStatusAndExpiresAtBefore(String status, LocalDateTime time); - boolean existsByLockIdAndStatus(UUID lockId, String status); -} diff --git a/xxxthegame/src/main/resources/static/activelock.html b/xxxthegame/src/main/resources/static/activelock.html index f4bef88..74ccee5 100644 --- a/xxxthegame/src/main/resources/static/activelock.html +++ b/xxxthegame/src/main/resources/static/activelock.html @@ -777,6 +777,16 @@ async function loadLock() { const res = await fetch('/keyholder/cardlock/' + lockId); + if (res.status === 404) { + // Prüfen, ob es ein TimeLock ist + const tlRes = await fetch('/keyholder/timelock/' + lockId); + if (tlRes.ok) { + window.location.replace('/activetimelock.html?lockId=' + lockId); + return; + } + document.getElementById('lockContent').textContent = 'Lock nicht gefunden.'; + return; + } if (!res.ok) { document.getElementById('lockContent').textContent = 'Lock nicht gefunden.'; return; diff --git a/xxxthegame/src/main/resources/static/activetimelock.html b/xxxthegame/src/main/resources/static/activetimelock.html new file mode 100644 index 0000000..8fc58f5 --- /dev/null +++ b/xxxthegame/src/main/resources/static/activetimelock.html @@ -0,0 +1,1091 @@ + + + + + + + TimeLock – XXX The Game + + + + + +
+
+ +

⏱ TimeLock Session

+ + + + + + + + +
+
Verbleibende Zeit
+
+ + +
+ + + + + + + + + + + + + +
Wird geladen…
+ +
+ +
+
+ + +
+
+
+

+

+ +
+
+ + +
+
+
🚿
+

Hygiene-Öffnung

+ +
+

Dein aktueller Entsperrcode:

+
+
+
Verbleibende Zeit
+
+
+ +
+ + +
+
+ + + + + +
+
+

🔓 Lock beenden?

+

Dein Entsperrcode:

+
+
+ + +
+
+
+ + +
+
+

🆘 Notfall-Entsperrung

+
+
+ + +
+
+
+ + + + + + + diff --git a/xxxthegame/src/main/resources/static/communityvotes.html b/xxxthegame/src/main/resources/static/communityvotes.html index d16a0c6..5e54752 100644 --- a/xxxthegame/src/main/resources/static/communityvotes.html +++ b/xxxthegame/src/main/resources/static/communityvotes.html @@ -19,7 +19,6 @@ margin-bottom: 1.5rem; } - /* ── Unified feed ── */ #feed { display: flex; flex-direction: column; @@ -87,7 +86,7 @@ color: var(--color-primary); background: none; } - .vote-btn.voted-up { border-color: #2ecc71; color: #2ecc71; background: rgba(46,204,113,0.08); } + .vote-btn.voted-up { border-color: #2ecc71; color: #2ecc71; background: rgba(46,204,113,0.08); } .vote-btn.voted-down { border-color: #e74c3c; color: #e74c3c; background: rgba(231,76,60,0.08); } .vote-btn:disabled { opacity: 0.55; cursor: not-allowed; pointer-events: none; } .vote-count { font-weight: 600; font-size: 0.88rem; } @@ -107,7 +106,7 @@ flex-wrap: wrap; gap: 0.4rem; } - .task-vote-lockee { font-weight: 600; font-size: 0.92rem; } + .task-vote-lockee { font-weight: 600; font-size: 0.92rem; } .task-vote-expires { font-size: 0.78rem; color: var(--color-muted); } .task-vote-options { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.5rem; } .task-vote-btn { @@ -150,6 +149,30 @@ text-align: center; } + /* ── Pranger-Karte ── */ + .pillory-card { + background: var(--color-card); + border: 1px solid rgba(231,76,60,0.35); + border-radius: 10px; + padding: 0.85rem 1rem; + } + .pillory-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.4rem; + flex-wrap: wrap; + gap: 0.4rem; + } + .pillory-lockee { font-weight: 600; font-size: 0.92rem; } + .pillory-date { font-size: 0.78rem; color: var(--color-muted); } + .pillory-reason { + font-size: 0.82rem; + color: #e74c3c; + margin-bottom: 0.25rem; + } + .pillory-message { font-size: 0.88rem; } + .empty-hint { color: var(--color-muted); font-size: 0.9rem; @@ -170,7 +193,7 @@
Community Votes
-
Verifikationen & Aufgaben-Abstimmungen
+
Verifikationen, Aufgaben-Abstimmungen & Pranger
@@ -186,36 +209,84 @@ function esc(s) { return String(s || '').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); } - function fmtDateTime(isoStr) { return new Date(isoStr).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}); } - // ── Task vote card builder ───────────────────────────────────────────────── + // ── Verifikations-Karte ──────────────────────────────────────────────────── - function buildTaskVoteCard(vote) { - const isOwn = vote.isOwnLock; - const alreadyVoted = vote.myVote !== null && vote.myVote !== undefined; + function buildVerCard(base, detail) { + const voted = detail.isOwnLock || detail.myVote !== null && detail.myVote !== undefined; + const votedUp = !detail.isOwnLock && detail.myVote === true; + const votedDn = !detail.isOwnLock && detail.myVote === false; + const id = base.displayId; + + const card = document.createElement('div'); + card.className = 'vote-card'; + card.innerHTML = ` +
+ Verifikationsbild +
${esc(detail.code)}
+
+
+
Verifikation · ${esc(base.lockeeName)} · ${fmtDateTime(base.createdAt)}
+
+ + +
+
`; + return card; + } + + async function castVerVote(displayId, upvote) { + document.getElementById('up-' + displayId).disabled = true; + document.getElementById('dn-' + displayId).disabled = true; + + const res = await fetch(`/games/chastity/community/verification/${displayId}/vote/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ upvote }) + }); + if (res.ok || res.status === 202) { + const countEl = document.getElementById(upvote ? 'upcount-' + displayId : 'dncount-' + displayId); + countEl.textContent = parseInt(countEl.textContent) + 1; + document.getElementById((upvote ? 'up-' : 'dn-') + displayId) + .classList.add(upvote ? 'voted-up' : 'voted-down'); + } + } + + // ── Aufgaben-Abstimmungs-Karte ───────────────────────────────────────────── + + function buildTaskVoteCard(base, detail) { + const isOwn = detail.isOwnLock; + const alreadyVoted = detail.entries.some(e => e.ownVote); + const id = base.displayId; let optionsHtml = ''; - (vote.tasks || []).forEach((t, i) => { - const count = (vote.voteCounts || [])[i] || 0; - const isMyVote = vote.myVote === i; - const desc = t.description - ? `
${esc(t.description)}
` + (detail.entries || []).forEach((e, i) => { + const desc = e.description + ? `
${esc(e.description)}
` : ''; - const mins = t.minutes > 0 - ? ` ⏱ ${t.minutes} Min.` + const mins = e.minutes > 0 + ? ` ⏱ ${e.minutes} Min.` : ''; - optionsHtml += ``; }); @@ -225,181 +296,107 @@ const card = document.createElement('div'); card.className = 'task-vote-card'; - card.dataset.ts = vote.createdAt; card.innerHTML = `
- 🃏 ${esc(vote.lockeeName)} - Endet: ${fmtDateTime(vote.expiresAt)} + 🃏 ${esc(base.lockeeName)} + Endet: ${fmtDateTime(detail.expiresAt)}
${optionsHtml}
${ownHint}`; return card; } - async function castTaskVote(voteSessionId, taskIndex) { - document.querySelectorAll(`[id^="tvbtn-${voteSessionId}-"]`).forEach(btn => btn.disabled = true); + async function castTaskVote(displayId, taskIndex) { + document.querySelectorAll(`[id^="tvbtn-${displayId}-"]`).forEach(btn => btn.disabled = true); - const res = await fetch(`/task-card/community/votes/${voteSessionId}/vote/${taskIndex}`, { method: 'POST' }); + const res = await fetch(`/games/chastity/community/taskvote/${displayId}/vote/${taskIndex}`, { method: 'POST' }); if (res.ok || res.status === 204) { - const countEl = document.getElementById(`tvcount-${voteSessionId}-${taskIndex}`); + const countEl = document.getElementById(`tvcount-${displayId}-${taskIndex}`); if (countEl) { const next = (parseInt(countEl.textContent) || 0) + 1; countEl.textContent = `${next} Stimme${next !== 1 ? 'n' : ''}`; } - const btn = document.getElementById(`tvbtn-${voteSessionId}-${taskIndex}`); - if (btn) btn.classList.add('my-vote'); + document.getElementById(`tvbtn-${displayId}-${taskIndex}`)?.classList.add('my-vote'); } } - // ── Verification card builder ────────────────────────────────────────────── + // ── Pranger-Karte ────────────────────────────────────────────────────────── - function buildVerCard(item) { - const isOwn = item.myVote === 'own'; - const voted = isOwn || (item.myVote !== null && item.myVote !== undefined); - const votedUp = !isOwn && item.myVote === true; - const votedDn = !isOwn && item.myVote === false; + const PILLORY_LABELS = { + HYGIENE_OPENING_EXEEDED: 'Hygiene-Öffnung überschritten', + KEYHOLDER_DESCESSION: 'Keyholder hat aufgegeben' + }; + function buildPilloryCard(base, detail) { const card = document.createElement('div'); - card.className = 'vote-card'; - card.dataset.ts = item.verificationTime; + card.className = 'pillory-card'; card.innerHTML = ` -
- Verifikationsbild -
${esc(item.code)}
+
+ 🔒 ${esc(base.lockeeName)} + ${fmtDateTime(base.createdAt)}
-
-
Verifikation · ${fmtDateTime(item.verificationTime)}
-
- - -
-
`; +
⚠️ ${esc(PILLORY_LABELS[detail.reason] || detail.reason)}
+ ${detail.message ? `
${esc(detail.message)}
` : ''}`; return card; } - async function castVerVote(verificationId, upvote) { - const upBtn = document.getElementById('up-' + verificationId); - const dnBtn = document.getElementById('dn-' + verificationId); - upBtn.disabled = true; - dnBtn.disabled = true; + // ── Unified feed mit Paging ──────────────────────────────────────────────── - const res = await fetch('/verification/' + verificationId + '/vote/', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ upvote }) - }); - if (res.ok || res.status === 202) { - const countEl = document.getElementById(upvote ? 'upcount-' + verificationId : 'dncount-' + verificationId); - countEl.textContent = parseInt(countEl.textContent) + 1; - (upvote ? upBtn : dnBtn).classList.add(upvote ? 'voted-up' : 'voted-down'); - } + let page = 0; + let exhausted = false; + let loading = false; + let rendered = 0; + + async function fetchDetail(base) { + const urls = { + VERIFICATION: `/games/chastity/community/verification/${base.displayId}`, + TASK_VOTE: `/games/chastity/community/taskvote/${base.displayId}`, + PILLORY: `/games/chastity/community/pillory/${base.displayId}` + }; + const url = urls[base.type]; + if (!url) return null; + try { + const r = await fetch(url); + return r.ok ? await r.json() : null; + } catch(e) { return null; } } - // ── Unified feed ────────────────────────────────────────────────────────── - // Strategy: task votes are loaded once (small set); verifications are paginated. - // When each verification page loads, we interleave the pending task votes that - // belong chronologically before the oldest verification on this page. - - let verPage = 0; - let verExhausted = false; - let loading = false; - // Task votes sorted newest-first; items are removed as they get placed in feed - let pendingTaskVotes = []; - let taskVotesLoaded = false; - let totalRendered = 0; - - function getTs(isoStr) { return new Date(isoStr).getTime(); } - - // Append cards to feed in the correct order. - // `items` must already be sorted newest-first. - function appendItems(items) { - const feed = document.getElementById('feed'); - items.forEach(card => feed.appendChild(card)); - totalRendered += items.length; - } - - // Merge verItems (sorted newest-first) with the front of pendingTaskVotes - // (also sorted newest-first). Only consume task votes that are >= cutoffMs - // so that older task votes wait for later verification pages. - // Pass cutoffMs = -Infinity to flush all remaining task votes. - function mergeWithTaskVotes(verItems, cutoffMs) { - const result = []; - let vIdx = 0; - while (vIdx < verItems.length || (pendingTaskVotes.length > 0 && getTs(pendingTaskVotes[0].createdAt) >= cutoffMs)) { - const verTs = vIdx < verItems.length ? getTs(verItems[vIdx].verificationTime) : -Infinity; - const tvTs = pendingTaskVotes.length > 0 && getTs(pendingTaskVotes[0].createdAt) >= cutoffMs - ? getTs(pendingTaskVotes[0].createdAt) - : -Infinity; - - if (tvTs >= verTs) { - result.push(buildTaskVoteCard(pendingTaskVotes.shift())); - } else if (vIdx < verItems.length) { - result.push(buildVerCard(verItems[vIdx++])); - } else { - break; - } - } - // Remaining verifications (task votes exhausted or below cutoff) - while (vIdx < verItems.length) { - result.push(buildVerCard(verItems[vIdx++])); - } - return result; + function buildCard(base, detail) { + if (!detail) return null; + if (base.type === 'VERIFICATION') return buildVerCard(base, detail); + if (base.type === 'TASK_VOTE') return buildTaskVoteCard(base, detail); + if (base.type === 'PILLORY') return buildPilloryCard(base, detail); + return null; } async function loadMore() { - if (loading || verExhausted) return; + if (loading || exhausted) return; loading = true; document.getElementById('loadSpinner').style.display = ''; - // Load task votes on first call - if (!taskVotesLoaded) { - try { - const r = await fetch('/task-card/community/votes'); - if (r.ok) { - const votes = await r.json(); - pendingTaskVotes = votes.sort((a, b) => getTs(b.createdAt) - getTs(a.createdAt)); - } - } catch(e) {} - taskVotesLoaded = true; - } - - let verItems = []; + let pageData; try { - const r = await fetch('/verification/community?page=' + verPage); - if (r.ok) verItems = await r.json(); - } catch(e) {} + const r = await fetch(`/games/chastity/community?page=${page}&sort=createdAt,desc`); + if (!r.ok) { loading = false; document.getElementById('loadSpinner').style.display = 'none'; return; } + pageData = await r.json(); + } catch(e) { loading = false; document.getElementById('loadSpinner').style.display = 'none'; return; } + + const items = pageData.content || []; + if (pageData.last) exhausted = true; + page++; + + const details = await Promise.all(items.map(fetchDetail)); document.getElementById('loadSpinner').style.display = 'none'; loading = false; - let batch; - if (verItems.length === 0) { - verExhausted = true; - batch = mergeWithTaskVotes([], -Infinity); - } else { - const oldestVerTs = getTs(verItems[verItems.length - 1].verificationTime); - if (verItems.length < 10) { - verExhausted = true; - // Last page: flush all remaining task votes too - batch = mergeWithTaskVotes(verItems, -Infinity); - } else { - verPage++; - // Only include task votes newer than the oldest item in this page - batch = mergeWithTaskVotes(verItems, oldestVerTs); - } - } + const feed = document.getElementById('feed'); + items.forEach((base, i) => { + const card = buildCard(base, details[i]); + if (card) { feed.appendChild(card); rendered++; } + }); - if (batch.length > 0) appendItems(batch); - - if (totalRendered === 0 && verExhausted) { + if (rendered === 0 && exhausted) { document.getElementById('emptyHint').style.display = ''; } } diff --git a/xxxthegame/src/main/resources/static/img/card_cum.png b/xxxthegame/src/main/resources/static/img/card_cum.png new file mode 100644 index 0000000..25828fa Binary files /dev/null and b/xxxthegame/src/main/resources/static/img/card_cum.png differ diff --git a/xxxthegame/src/main/resources/static/img/card_cum_caged.png b/xxxthegame/src/main/resources/static/img/card_cum_caged.png new file mode 100644 index 0000000..5da6f37 Binary files /dev/null and b/xxxthegame/src/main/resources/static/img/card_cum_caged.png differ diff --git a/xxxthegame/src/main/resources/static/js/card-defs.js b/xxxthegame/src/main/resources/static/js/card-defs.js index 674f81d..65f3579 100644 --- a/xxxthegame/src/main/resources/static/js/card-defs.js +++ b/xxxthegame/src/main/resources/static/js/card-defs.js @@ -62,6 +62,22 @@ const CARD_DEFS = [ defMin: 0, defMax: 0, }, + { + id: 'CUM', + img: '/img/card_cum.png', + name: 'Cum', + desc: 'Spezielle Karte.', + defMin: 0, + defMax: 0, + }, + { + id: 'CUM_IN_CAGE', + img: '/img/card_cum_caged.png', + name: 'Cum in Cage', + desc: 'Spezielle Karte.', + defMin: 0, + defMax: 0, + }, ]; /** Lookup-Objekt für Konsumenten, die nach ID auf Name/Bild/Beschreibung zugreifen. */ diff --git a/xxxthegame/src/main/resources/static/meine-locks.html b/xxxthegame/src/main/resources/static/meine-locks.html index b52eb87..dbbb0f9 100644 --- a/xxxthegame/src/main/resources/static/meine-locks.html +++ b/xxxthegame/src/main/resources/static/meine-locks.html @@ -8,6 +8,7 @@
-
+

Meine Locks

- + +
- +