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.minimessage.Template; 034import org.checkerframework.checker.nullness.qual.NonNull; 035 036import javax.annotation.Nullable; 037import java.util.concurrent.atomic.AtomicBoolean; 038 039/** 040 * The default PlotSquared Progress Subscriber. Can be used for both console and player tasks. 041 * It is the {@link ProgressSubscriber} returned by {@link com.plotsquared.core.inject.factory.ProgressSubscriberFactory}. 042 * Runs a repeating synchronous task notifying the given actor about any updates, saving updates notified by the ChunkCoordinator. 043 */ 044public class DefaultProgressSubscriber implements ProgressSubscriber { 045 046 @NonNull 047 private final AtomicDouble progress = new AtomicDouble(0); 048 @NonNull 049 private final AtomicBoolean started = new AtomicBoolean(false); 050 @NonNull 051 private final AtomicBoolean cancelled = new AtomicBoolean(false); 052 @NonNull 053 private final TaskTime interval; 054 @NonNull 055 private final TaskTime wait; 056 @NonNull 057 private final PlotPlayer<?> actor; 058 @NonNull 059 private final Caption caption; 060 private PlotSquaredTask task; 061 062 @AssistedInject 063 public DefaultProgressSubscriber() { 064 throw new UnsupportedOperationException("DefaultProgressSubscriber cannot be used without an actor."); 065 } 066 067 @AssistedInject 068 public DefaultProgressSubscriber(@Nullable @Assisted("subscriber") final PlotPlayer<?> actor) { 069 Preconditions.checkNotNull( 070 actor, 071 "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!" 072 ); 073 this.actor = actor; 074 this.interval = TaskTime.ms(Settings.QUEUE.NOTIFY_INTERVAL); 075 this.wait = TaskTime.ms(Settings.QUEUE.NOTIFY_WAIT); 076 this.caption = TranslatableCaption.of("working.progress"); 077 } 078 079 @AssistedInject 080 public DefaultProgressSubscriber( 081 @Nullable @Assisted("subscriber") final PlotPlayer<?> actor, 082 @Assisted("progressInterval") final long interval, 083 @Assisted("waitBeforeStarting") final long wait, 084 @Nullable @Assisted("caption") final Caption caption 085 ) { 086 Preconditions.checkNotNull( 087 actor, 088 "Actor cannot be null when using DefaultProgressSubscriber! Make sure if attempting to use custom Subscribers it is correctly parsed to the queue!" 089 ); 090 this.actor = actor; 091 this.interval = TaskTime.ms(interval); 092 this.wait = TaskTime.ms(wait); 093 if (caption == null) { 094 this.caption = TranslatableCaption.of("working.progress"); 095 } else { 096 this.caption = caption; 097 } 098 } 099 100 @Override 101 public void notifyProgress(@NonNull ChunkCoordinator coordinator, double progress) { 102 this.progress.set(progress); 103 if (started.compareAndSet(false, true)) { 104 TaskManager.getPlatformImplementation().taskLater(() -> task = TaskManager 105 .getPlatformImplementation() 106 .taskRepeat(() -> { 107 if (!started.get()) { 108 return; 109 } 110 if (cancelled.get()) { 111 task.cancel(); 112 return; 113 } 114 actor.sendMessage( 115 caption, 116 Template.of("progress", String.format("%.2f", this.progress.doubleValue() * 100)) 117 ); 118 }, interval), wait); 119 } 120 } 121 122 public void notifyEnd() { 123 cancel(); 124 } 125 126 public void cancel() { 127 this.cancelled.set(true); 128 if (this.task != null) { 129 task.cancel(); 130 } 131 } 132 133}