001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.queue.subscriber; 020 021import com.google.common.base.Preconditions; 022import com.google.common.util.concurrent.AtomicDouble; 023import com.google.inject.assistedinject.Assisted; 024import com.google.inject.assistedinject.AssistedInject; 025import com.plotsquared.core.configuration.Settings; 026import com.plotsquared.core.configuration.caption.Caption; 027import com.plotsquared.core.configuration.caption.TranslatableCaption; 028import com.plotsquared.core.player.PlotPlayer; 029import com.plotsquared.core.queue.ChunkCoordinator; 030import com.plotsquared.core.util.task.PlotSquaredTask; 031import com.plotsquared.core.util.task.TaskManager; 032import com.plotsquared.core.util.task.TaskTime; 033import net.kyori.adventure.text.Component; 034import net.kyori.adventure.text.minimessage.tag.Tag; 035import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 036import org.checkerframework.checker.nullness.qual.NonNull; 037 038import javax.annotation.Nullable; 039import java.util.Objects; 040import java.util.concurrent.atomic.AtomicBoolean; 041 042/** 043 * The default PlotSquared Progress Subscriber. Can be used for both console and player tasks. 044 * It is the {@link ProgressSubscriber} returned by {@link com.plotsquared.core.inject.factory.ProgressSubscriberFactory}. 045 * Runs a repeating synchronous task notifying the given actor about any updates, saving updates notified by the ChunkCoordinator. 046 */ 047public class DefaultProgressSubscriber implements ProgressSubscriber { 048 049 @NonNull 050 private final AtomicDouble progress = new AtomicDouble(0); 051 @NonNull 052 private final AtomicBoolean started = new AtomicBoolean(false); 053 @NonNull 054 private final AtomicBoolean cancelled = new AtomicBoolean(false); 055 @NonNull 056 private final TaskTime interval; 057 @NonNull 058 private final TaskTime wait; 059 @NonNull 060 private final PlotPlayer<?> actor; 061 @NonNull 062 private final Caption caption; 063 private PlotSquaredTask task; 064 065 @AssistedInject 066 public DefaultProgressSubscriber() { 067 throw new UnsupportedOperationException("DefaultProgressSubscriber cannot be used without an actor."); 068 } 069 070 @AssistedInject 071 public DefaultProgressSubscriber(@Nullable @Assisted("subscriber") final PlotPlayer<?> actor) { 072 Preconditions.checkNotNull( 073 actor, 074 "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!" 075 ); 076 this.actor = actor; 077 this.interval = TaskTime.ms(Settings.QUEUE.NOTIFY_INTERVAL); 078 this.wait = TaskTime.ms(Settings.QUEUE.NOTIFY_WAIT); 079 this.caption = TranslatableCaption.of("working.progress"); 080 } 081 082 @AssistedInject 083 public DefaultProgressSubscriber( 084 @Nullable @Assisted("subscriber") final PlotPlayer<?> actor, 085 @Assisted("progressInterval") final long interval, 086 @Assisted("waitBeforeStarting") final long wait, 087 @Nullable @Assisted("caption") final Caption caption 088 ) { 089 Preconditions.checkNotNull( 090 actor, 091 "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!" 092 ); 093 this.actor = actor; 094 this.interval = TaskTime.ms(interval); 095 this.wait = TaskTime.ms(wait); 096 this.caption = Objects.requireNonNullElseGet(caption, () -> TranslatableCaption.of("working.progress")); 097 } 098 099 @Override 100 public void notifyProgress(@NonNull ChunkCoordinator coordinator, double progress) { 101 this.progress.set(progress); 102 if (started.compareAndSet(false, true)) { 103 TaskManager.getPlatformImplementation().taskLater(() -> task = TaskManager 104 .getPlatformImplementation() 105 .taskRepeat(() -> { 106 if (!started.get()) { 107 return; 108 } 109 if (cancelled.get()) { 110 task.cancel(); 111 return; 112 } 113 actor.sendMessage( 114 caption, 115 TagResolver.resolver( 116 "progress", 117 Tag.inserting(Component.text(String.format("%.2f", this.progress.doubleValue() * 100))) 118 ) 119 ); 120 }, interval), wait); 121 } 122 } 123 124 public void notifyEnd() { 125 cancel(); 126 } 127 128 public void cancel() { 129 this.cancelled.set(true); 130 if (this.task != null) { 131 task.cancel(); 132 } 133 } 134 135}