Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ deflated prices and a lot more!
- [Add untracked trades easily](#add-untracked-trades-easily)
- [Setup offers quickly](#offer-editors)
- [Favorites lookup](#favorites-lookup)
- [Development](#development)
+ [General Structure of Codebase](#general-structure-of-codebase)
+ [Example Flow](#example-flow)


# Features
The plugin is divided into three tabs: the slots tab, flipping tab and statistics tab.
Expand Down Expand Up @@ -122,6 +126,108 @@ Quickly lookup your favorited items just by typing "1" in the ge search!
<img src = "https://github.com/Flipping-Utilities/rl-plugin/blob/master/images/lookup.png">
</p>

# Development

### General Structure of Codebase

This section will talk about the purpose of various parts of the codebase, specifically the folders.

**controller/**
- This folder contains the components that handle some specific responsibility of the plugin, mainly by handling runelite
events (such as new GE offers) or presenting APIs to alter/view user data. Each class in this folder is instantiated
by the FlippingPlugin in its constructor, which is run on client startup. Each of these classes handles a specific
responsibility of the plugin. For example, the NewOfferEventPipelineHandler is responsible for consuming new offer
events and adding it to the data structures that model user trade history. The classes are used via the FlippingPlugin
calling their methods, for example, when the FlippingPlugin gets an offer event in the onGrandExchangeOfferChanged
method it calls `newOfferEventPipelineHandler.onGrandExchangeOfferChanged(newOffer);`.
Much of the logic in these controller classes used to live in the FlippingPlugin class but was moved out as the
FlippingPlugin class had become huge and was doing too many things.


**model/**
- This folder contains the classes that represent the data that will be stored on disk. These classes are turned into
JSON and saved to disk and are also created from JSON that was previously saved to disk. To see what each of these
classes actually model, check out the [Data model](#Data-model) section.


**db/**
- This folder contains the class responsible for taking the models and saving them
to disk as JSON. It also loads JSON from disk (previously saved) and turns them into objects.

**ui/**
- This folder contains all of the UI code for the plugin which is the code that draws the "plugin" you see, such as the slots
tab, the flipping tab, the statistics tab, and all of the content within them.

**jobs/**
- This folder contains code that is running in background threads to periodically performing some action, such as the
code that queries the wiki to get the new prices of items, or the code that sends premium users' slots to our api.

**utilities/**
- Just random code that doesn't fit neatly into any of the categories above.

### Example Flow

This section will give an example of how the plugin actually runs, what happens during its lifetime, how the different
components interact with each other, and how the data flows.

The starting point to this plugin is the FlippingPlugin class. On Runelite startup, the Runelite code will create an
instance of the FlippingPlugin class and then call its `startUp()` method.

#### On Runelite Client Startup
The FlippingPlugin does three main things on startup (all in the `startUp()` method):
1. It creates instances of all the controller classes, described in the controller section of [General Structure of Codebase](#General-structure-of-codebase)
2. It initializes the various UI classes such as the FlippingPanel and StatsPanel. This will result in the UI being
drawn eventually.
3. It loads the user's previously saved trading history from the disk via one of the controller classes, the DataHandler

Now things have been setup properly and the UI has been drawn and populated with the user's previously saved data from
disk.

#### During Runelite Client's Lifetime
During the lifetime of the Runelite Client (between startup and shutdown), there are primarily three ways the plugin does work
1. **Handling Runelite events that reflect some change of state in the game**. There are many different Runelite events. For example,
events can trigger in various scenarios: when an account logs in, a GE offer gets placed, an account logs out,
an account opens the GE interace, and many more. The Runelite code will feed the plugin these events if the plugin implements certain methods. For example,
when the user places an offer in the GE, Runelite code gives the plugin details of the GE offer event via calling the
`onGrandExchangeOfferChanged` on the FlippingPlugin class and passing that method a `GrandExchangeOfferChanged` object.
It knows to do this because of method naming conventions. You don't have to define a method to handle every possible
type of Runelite event, just the ones your plugin should care about.
2. **Handling UI interactions that the user initiates**. For example, the user may want to see data for another account, so
he clicks on the account dropdown selector and selects another account. The plugin then needs to re-render its UI with the
selected account's trades.
3. **Via background threads that are performing some action periodically**. See the jobs section of [General Structure of Codebase](#general-structure-of-codebase).


#### On Runelite Client Shutdown
The runelite client will tell the plugin when its shutting down and will allow it to execute some code before
that happens. This occurs in `onClientShutdown` in the FlippingPlugin class. Not much happens other than saving
user data to disk and cancelling any background jobs that were running.


### Data model

This section describes how the plugin models users' trade history. The main model classes used to do this are:
AccountData, FlippingItem, HistoryManager, and OfferEvent.

When you go to `.runelite/flipping/<username>.json` and open it, you will see the JSON version of an AccountData
object. Each of the user's osrs accounts get their own AccountData object, each of which is stored in a file
of the format `.runelite/flipping/<account_username>.json`.

AccountData objects are created from the JSON in those files on client startup (or created on account login if they have
no previously saved data for that account). As the user makes trades, deletes trades via the UI, and so on, the
AccountData object for the correct account is mutated. Then, on account logout or client shutdown, it is turned back
into JSON and saved into the same file it was loaded from (or creates a new file if there was no previously saved data).

The AccountData object's most important field is `List<FlippingItem> trades`. This field contains FlippingItem objects.
FlippingItem objects have an itemId (every runescape item has a unique number to identify it) and represents the trade history
for that item on that account. So, for example, if you flipped dragon claws on an account, you would have one and only one
FlippingItem in that account's AccountData object that holds all the buy and sell offers you have ever made for dragon
claws on that account.

The FlippingItem stores all the offers for that item in the `HistoryManager history` field. Put simply, a HistoryManager
is just a list of OfferEvent. An OfferEvent represents a buy and sell offer in which some amount of an
item bought or sold. It has various fields like price, quantity, etc.


## Icon Attributions
All icons were either made by Belieal or downloaded from the creators on www.flaticon.com below.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repositories {
mavenCentral()
}

def runeLiteVersion = '1.8.32'
def runeLiteVersion = '1.9.5'

dependencies {
compileOnly group: 'net.runelite', name:'client', version: runeLiteVersion
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
31 changes: 14 additions & 17 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
Expand Down Expand Up @@ -129,6 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
Expand All @@ -154,19 +156,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

Expand All @@ -175,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi

exec "$JAVACMD" "$@"
25 changes: 7 additions & 18 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

Expand All @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Expand All @@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
Expand All @@ -61,28 +64,14 @@ echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
Expand Down
Binary file added images/list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 39 additions & 2 deletions src/main/java/com/flippingutilities/model/AccountWideData.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.*;

@Data
@Slf4j
public class AccountWideData {
List<Option> options = new ArrayList<>();
List<Section> sections = new ArrayList<>();
Map<String, Set<Integer>> favoriteItemLists = new HashMap<>();
boolean shouldMakeNewAdditions = true;
boolean enhancedSlots = true;
String jwt;
Expand All @@ -37,6 +37,12 @@ public boolean setDefaults() {
didChangeData = true;
setDefaultFlippingItemPanelSections();
}

if (favoriteItemLists.isEmpty()) {
didChangeData = true;
favoriteItemLists.put("DEFAULT", new HashSet<>());
}

return didChangeData;
}

Expand Down Expand Up @@ -92,4 +98,35 @@ private void setDefaultFlippingItemPanelSections() {
sections.add(otherSection);
}

public Set<Integer> getFavoriteListData(String listName) {
return favoriteItemLists.get(listName);
}

public Set<String> getAllListNames () {
return favoriteItemLists.keySet();
}

public boolean addNewFavoriteList (String listName){
if (!favoriteItemLists.containsKey(listName)){
favoriteItemLists.put(listName,new HashSet<>());
return true;
}
else{
return false;
}
}

public void addItemToFavoriteList(String listName, int itemId){
Set<Integer> list = favoriteItemLists.get(listName);
list.add(itemId);
}

public void removeItemFromList(String listName, int itemId) {
Set<Integer> set = favoriteItemLists.get(listName);
set.remove(itemId);
}

public void deleteItemList(String listName){
favoriteItemLists.remove(listName);
}
}
20 changes: 19 additions & 1 deletion src/main/java/com/flippingutilities/model/FlippingItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@

import java.time.Instant;
import java.util.*;
import java.util.function.Predicate;

/**
* This class is the representation of an item that a user is flipping. It contains information about the
Expand Down Expand Up @@ -119,6 +120,10 @@ public class FlippingItem implements Searchable
@Setter
private transient Boolean expand;

@Getter
private List<String> favoriteLists = new ArrayList<>();


public FlippingItem(int itemId, String itemName, int totalGeLimit, String flippedBy)
{
this.latestInstaBuy = Optional.empty();
Expand Down Expand Up @@ -148,7 +153,8 @@ public FlippingItem clone()
latestBuy,
latestSell,
latestActivityTime,
expand);
expand,
favoriteLists);
}

/**
Expand Down Expand Up @@ -389,4 +395,16 @@ public void hydrate(int geLimit) {
public String getNameForSearch() {
return itemName;
}

public boolean itemBelongsToList(String listName){
return favoriteLists.contains(listName);
}

public void addItemToList(String listName){
favoriteLists.add(listName);
}

public void removeItemFromList(String listName){
favoriteLists.remove(listName);
}
}
Loading