message: Add spaces after period

diff:
@@ -11196,8 +11196,8 @@ Analysis of routes between Europe and the USA
The next few queries show how you can use a graph like 'air-routes' do perform
analysis on a particular industry segment. The following queries analyze the
distribution and availability of routes between airports across Europe and airports
-in the United States.First of all let's just find out how many total routes there are
-between airports anywhere in Europe and airports in the USA.
+in the United States. First of all let's just find out how many total routes there
+are between airports anywhere in Europe and airports in the USA.
[source,groovy]
----
@@ -12016,13 +12016,13 @@ worthy of note. First of all it uses the preferred 'has' technique again to test
distance whereas in the previous query we used 'where'. Also this time we just list
the distance and the destination airport's code and we sort the end result using a
'sort'. We also use 'select' and 'as' rather than the perhaps more succinct 'path'
-and 'by' to show a different way of achieving effectively the same results.The 'path'
-and 'by' combination were introduced more recently into TinkerPop and I find that to
-be a more convenient syntax to use most of the time but both ways work and both have
-their benefits. We should also note that I show 'sort' being used here just to show a
-different way of ordering results, but in most cases we can re-write our query to use
-'order'. You will find several examples of 'order' being used throughout this
-book. Order is introduced in the "<<sort>>" section.
+and 'by' to show a different way of achieving effectively the same results. The
+'path' and 'by' combination were introduced more recently into TinkerPop and I find
+that to be a more convenient syntax to use most of the time but both ways work and
+both have their benefits. We should also note that I show 'sort' being used here just
+to show a different way of ordering results, but in most cases we can re-write our
+query to use 'order'. You will find several examples of 'order' being used throughout
+this book. Order is introduced in the "<<sort>>" section.
[source,groovy]
----

original_message: Add spaces after period


message: Fix "aiport" typo

change_type: MODIFY

diff:
@@ -2596,7 +2596,7 @@ called 'aus'.
[source,groovy]
----
-// Store the properties for the AUS aiport in the variable aus.
+// Store the properties for the AUS airport in the variable aus.
aus=g.V().has('code','AUS').valueMap().next()
----
@@ -6875,9 +6875,9 @@ xyz.addEdge('route',dfw)
In many cases it is more convenient, and also recommendd, to perform each of the
previous operations using just the traversal object 'g'. The following example does
-just that. We first create a new aiport vertex for our imaginary airport and store it's
-vertex in the variable 'xyz'. We can then use that stored value when we create the
-edge also using a traversal. As with many parts of the Gremlin language, there is
+just that. We first create a new airport vertex for our imaginary airport and store
+it's vertex in the variable 'xyz'. We can then use that stored value when we create
+the edge also using a traversal. As with many parts of the Gremlin language, there is
more than one way to achieve the same results.
[source,groovy]

license: Apache License 2.0

repo: krlawrence/graph

original_message: Fix "aiport" typo


message: Fix "fist" typo

diff:
@@ -6849,7 +6849,7 @@ the following pages.
Adding an airport (vertex) and a route (edge)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-The following code uses the 'graph' object that we created when we fist loaded the
+The following code uses the 'graph' object that we created when we first loaded the
air-routes graph to create a new airport vertex (node) and then adds a route (edge)
from it to the existing DFW vertex. We can specify the label name ('airport') and as
many properties as we wish to while creating the vertex. In this case we just provide

original_message: Fix "fist" typo


message: Fix "recommendd" typo

diff:
@@ -6873,7 +6873,7 @@ dfw = g.V().has('code','DFW').next()
xyz.addEdge('route',dfw)
----
-In many cases it is more convenient, and also recommendd, to perform each of the
+In many cases it is more convenient, and also recommended, to perform each of the
previous operations using just the traversal object 'g'. The following example does
just that. We first create a new aiport vertex for our imaginary airport and store it's
vertex in the variable 'xyz'. We can then use that stored value when we create the

original_message: Fix "recommendd" typo


message: Fix as/is typo

diff:
@@ -7312,7 +7312,7 @@ g.E().drop()
You could also use the 'graph' object to do this. The code below uses the graph
object to retrieve all of the edges and then iterates over them dropping them one by
-one. Again for very large graphs this may not be an ideal approach is this requires
+one. Again for very large graphs this may not be an ideal approach as this requires
reading all of the edge definitions into memory. Note that in this case we call the
'remove' method rather than use 'drop' as we are not using a graph traversal in this
case.

original_message: Fix as/is typo


message: Add missing have

diff:
@@ -7501,11 +7501,11 @@ Properties have IDs too
^^^^^^^^^^^^^^^^^^^^^^^
We have seen many examples already that show how both vertices and edges have a unique
-ID. What may not have been obvious however is that properties also an ID. Unlike vertex
-and edge IDs property IDs are not guaranteed to be unique across the graph. Certainly
-with TinkerGraph I have encountered cases where a vertex and a property share the same
-ID. This is not really an issue because they are used in different ways to access
-their associated graph element.
+ID. What may not have been obvious however is that properties also have an ID. Unlike
+vertex and edge IDs property IDs are not guaranteed to be unique across the graph.
+Certainly with TinkerGraph I have encountered cases where a vertex and a property
+share the same ID. This is not really an issue because they are used in different
+ways to access their associated graph element.
The query below returns the vertex property object (vp) for any property in the graph
that has a value of 'London'.

original_message: Add missing have


message: Fix "it we" to "if we"

diff:
@@ -8263,7 +8263,7 @@ g.V().has('airport','country','IE').aggregate('a').limit(2).cap('a')
----
Both 'aggregate' and 'store' can be followed by a 'by' modulator to specify more
-precisely what should be collected. For example, it we wanted to store the number of
+precisely what should be collected. For example, if we wanted to store the number of
runways that each airport in Ireland has we could do so as follows.
[source,groovy]

original_message: Fix "it we" to "if we"


message: Fix poving typo

diff:
@@ -9026,7 +9026,7 @@ g.V(3).sack(assign).by(constant(true)).sack(or).by(constant(false)).sack()
true
----
-While poving we can do boolean operations using constants is interesting it is
+While proving we can do boolean operations using constants is interesting it is
perhaps not that useful. Where this functionality becomes more interesting is if the
values being used come from, for example, a vertex property. So let's create a couple
of vertices that each have a boolean property.

original_message: Fix poving typo


message: Fix is shows/it shows

diff:
@@ -9405,7 +9405,7 @@ v[54854]
----
We can now write a query that uses a boolean operator to generate a result in our
-sack. The example below is a bit arbitrary but is shows how the boolean operators can
+sack. The example below is a bit arbitrary but it shows how the boolean operators can
be used. We start at the vertex with an ID of 3, initialize a 'sack' with the
constant 'true' and the use the 'and' operator against the 'happy' property of the
vertex with a label of 'happy'. We return the results in a path, which will contain

original_message: Fix is shows/it shows


message: Remove duplicit queries

diff:
@@ -10887,7 +10887,7 @@ often used in response to a user query where an almost real time answer is neede
Because the air routes graph is small - we can do even quite complex queries using
little more than a TinkerGraph and the Gremlin Console. This is not the case if your
graph contains billions of vertices and edges. That said, I have seen some OLTP type
-queries queries, such as those looking for routes to remote airports, that can take
+queries, such as those looking for routes to remote airports, that can take
several minutes to run in the TinkerGraph environment.
NOTE: The TinkerPop documentation has in depth coverage of the various OLAP

original_message: Remove duplicit queries


message: Add missing verb

diff:
@@ -14176,7 +14176,7 @@ you just as likely could use 'curl' or some other HTTP/REST type of technology.
that is a long winded way of saying that once you move beyond the basics and head
towards putting a system into production, there are a lot of options to consider.
-In this section you will a selection of examples from these more sophisticated
+In this section you will find a selection of examples from these more sophisticated
environments. The focus of this book so far has been to teach the Gremlin query and
traversal language, using the Gremlin Console and TinkerGraph as our learning
environment. However, it would be remiss to end our discussion without at least

original_message: Add missing verb


message: Remove double get

diff:
@@ -18783,7 +18783,7 @@ g.V().as('a').outE().has('dist',gt(8000)).
path().by('code').by('dist')
----
-When we run this query using the Gremlin console with TinkerGraph we get get
+When we run this query using the Gremlin console with TinkerGraph we get
back results that have been to a degree 'pretty printed' by the Console as
shown below.

original_message: Remove double get


message: Fix uppercase The

diff:
@@ -18539,7 +18539,7 @@ of this in a moment.
As with Gremlin Server, YAML files can be used to configure a remote connection from
the Gremlin Console. The Gremlin Console as well as the version that comes bundled
with JanusGraph includes a set of YAML files that can be used as-is or edited as
-needed. In order to connect The Gremlin Console to the Gremlin Server that we just
+needed. In order to connect the Gremlin Console to the Gremlin Server that we just
configured, the file 'remote.yaml' can be used. I had to make one change, as shown
below. I commented out the 'serializer' line and replaced it with a slightly modified
version. I had to do this because of the version issues I mentioned above. At time of

original_message: Fix uppercase The


message: Fix "for examples"

diff:
@@ -18841,7 +18841,7 @@ exact same query as the one we used above.
"meta":{}}}
----
-What is being returned is useful in some cases, for examples we can see the
+What is being returned is useful in some cases, for example we can see the
'a' and 'b' labels that we used in our query but in this case all we really
wanted was the last part with the airport codes and the distances. We could
decide to write code to process this JSON as-is (probably using a JSON helper

original_message: Fix "for examples"


message: Fix GrapphML typo

diff:
@@ -19473,7 +19473,7 @@ have provided a brief overview of a few of them. As you will see there are a num
of ways you can represent graph data using simple CSV files. There are also XML
format, JSON formats and many more. Of these, the one that still seems to be
supported across most tools and platforms is GraphML. Not all of the features offered
-by Apache TinkerPop can be expressed using GrapphML however. So let's take a look at
+by Apache TinkerPop can be expressed using GraphML however. So let's take a look at
some of the more commonly used formats.
[[csv]]

original_message: Fix GrapphML typo


message: bump snapshot 2.0.24-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.23</version>
+ <version>2.0.24-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.23</version>
+ <version>2.0.24-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

repo: swagger-api/swagger-parser

original_message: bump snapshot 2.0.24-SNAPSHOT


message: bump snapshot 2.0.25-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.24</version>
+ <version>2.0.25-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.24</version>
+ <version>2.0.25-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.25-SNAPSHOT


message: bump snapshot 2.0.26-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.25</version>
+ <version>2.0.26-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.25</version>
+ <version>2.0.26-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.26-SNAPSHOT


message: bump snapshot 2.0.27-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.26</version>
+ <version>2.0.27-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.26</version>
+ <version>2.0.27-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.27-SNAPSHOT


message: bump snapshot 2.0.28-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.27</version>
+ <version>2.0.28-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.27</version>
+ <version>2.0.28-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.28-SNAPSHOT


message: bump snapshot 2.0.29-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.28</version>
+ <version>2.0.29-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.28</version>
+ <version>2.0.29-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.29-SNAPSHOT


message: bump snapshot 2.0.30-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.29</version>
+ <version>2.0.30-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.29</version>
+ <version>2.0.30-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.30-SNAPSHOT


message: bump snapshot 2.0.31-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.30</version>
+ <version>2.0.31-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.30</version>
+ <version>2.0.31-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.31-SNAPSHOT


message: bump snapshot 2.0.32-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.31</version>
+ <version>2.0.32-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.31</version>
+ <version>2.0.32-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.32-SNAPSHOT


message: bump snapshot 2.0.33-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.32</version>
+ <version>2.0.33-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.32</version>
+ <version>2.0.33-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.33-SNAPSHOT


message: bump snapshot 2.0.34-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.33</version>
+ <version>2.0.34-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.0.33</version>
+ <version>2.0.34-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.0.34-SNAPSHOT


message: bump snapshot 2.1.1-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.1.0</version>
+ <version>2.1.1-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.1.0</version>
+ <version>2.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.1.1-SNAPSHOT


message: bump snapshot 2.1.2-SNAPSHOT

diff:
<parent>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.1.1</version>
+ <version>2.1.2-SNAPSHOT</version>
<relativePath>../..</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

diff:
<modelVersion>4.0.0</modelVersion>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-project</artifactId>
- <version>2.1.1</version>
+ <version>2.1.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>swagger-parser-project</name>
<developers>

original_message: bump snapshot 2.1.2-SNAPSHOT


message: allowing different ports for loopback redirect uris

diff:
@@ -24,6 +24,7 @@ use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
+use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -262,15 +263,8 @@ abstract class AbstractGrant implements GrantTypeInterface
ClientEntityInterface $client,
ServerRequestInterface $request
) {
- if (\is_string($client->getRedirectUri())
- && (\strcmp($client->getRedirectUri(), $redirectUri) !== 0)
- ) {
- $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
- throw OAuthServerException::invalidClient($request);
- } elseif (\is_array($client->getRedirectUri())
- && \in_array($redirectUri, $client->getRedirectUri(), true) === false
- ) {
- $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
+ $validator = new Rfc8252RedirectUriValidator($client);
+ if (!$validator->validateRedirectUri($redirectUri)) {
throw OAuthServerException::invalidClient($request);
}
}

change_type: ADD

diff:
+<?php
+
+
+namespace League\OAuth2\Server\RedirectUriValidators;
+
+
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+
+class Rfc8252RedirectUriValidator
+{
+
+ private $client;
+
+ public function __construct(ClientEntityInterface $client) {
+ $this->client = $client;
+ }
+
+ public function validateRedirectUri(string $redirectUri) {
+ $parsedUrl = $this->parseUrl($redirectUri);
+ if ($this->isLoopbackAddress($parsedUrl)) {
+ return $this->allowDifferentPort($parsedUrl);
+ } else {
+ return $this->matchFullUri($redirectUri);
+ }
+ }
+
+ private function isLoopbackAddress(array $parsedUri) {
+ return $parsedUri['scheme'] === 'http'
+ && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
+ }
+
+ private function matchFullUri(string $redirectUri) {
+ return \in_array($redirectUri, $this->getClientRedirectUris(), true);
+ }
+
+ private function allowDifferentPort(array $parsedUrl) {
+ foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
+ if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function parseUrl(string $url) {
+ $parsedUrl = parse_url($url);
+ $parsedUrl['port'] = 80;
+
+ return $parsedUrl;
+ }
+
+ private function getClientRedirectUris() {
+ $clientRedirectUri = $this->client->getRedirectUri();
+ if (\is_string($clientRedirectUri)) {
+ return [ $clientRedirectUri ];
+ } else if (\is_array($clientRedirectUri)) {
+ return $clientRedirectUri;
+ } else {
+ return [];
+ }
+ }
+
+}

diff:
+<?php
+
+
+namespace LeagueTests\RedirectUriValidators;
+
+
+use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
+use LeagueTests\Stubs\ClientEntity;
+use PHPUnit\Framework\TestCase;
+
+class Rfc8252RedirectUriValidatorTest extends TestCase
+{
+ public function testInvalidNonLoopbackUri()
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri([
+ 'https://example.com:8443/endpoint',
+ 'https://example.com/different/endpoint',
+ ]);
+ $redirectUri = 'https://example.com/endpoint';
+
+ $validator = new Rfc8252RedirectUriValidator($client);
+
+ $this->assertFalse($validator->validateRedirectUri($redirectUri),
+ 'Non loopback URI must match in every part');
+ }
+
+ public function testValidNonLoopbackUri()
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri([
+ 'https://example.com:8443/endpoint',
+ 'https://example.com/different/endpoint',
+ ]);
+ $redirectUri = 'https://example.com:8443/endpoint';
+
+ $validator = new Rfc8252RedirectUriValidator($client);
+
+ $this->assertTrue($validator->validateRedirectUri($redirectUri),
+ 'Redirect URI must be valid when matching in every part');
+ }
+
+ public function testInvalidLoopbackUri()
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri('http://127.0.0.1:8443/endpoint');
+
+ $redirectUri = 'http://127.0.0.1:8443/different/endpoint';
+
+ $validator = new Rfc8252RedirectUriValidator($client);
+
+ $this->assertFalse($validator->validateRedirectUri($redirectUri),
+ 'Valid loopback redirect URI can change only the port number');
+ }
+
+ public function testValidLoopbackUri()
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri('http://127.0.0.1:8443/endpoint');
+
+ $redirectUri = 'http://127.0.0.1:8080/endpoint';
+
+ $validator = new Rfc8252RedirectUriValidator($client);
+
+ $this->assertTrue($validator->validateRedirectUri($redirectUri),
+ 'Loopback redirect URI can change the port number');
+ }
+}

license: MIT License


message: added test case for ipv6 loopback address

diff:
@@ -65,4 +65,17 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$this->assertTrue($validator->validateRedirectUri($redirectUri),
'Loopback redirect URI can change the port number');
}
+
+ public function testValidIpv4LoopbackUri()
+ {
+ $client = new ClientEntity();
+ $client->setRedirectUri('http://[::1]:8443/endpoint');
+
+ $redirectUri = 'http://[::1]:8080/endpoint';
+
+ $validator = new Rfc8252RedirectUriValidator($client);
+
+ $this->assertTrue($validator->validateRedirectUri($redirectUri),
+ 'Loopback redirect URI can change the port number');
+ }
}


message: fixed comments, names and changelog

diff:
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
+### Fixed
+The server will now validate redirect uris according to rfc8252 (PR #1203)
+
### Fixed
- The server will now only recognise and handle an authorization header if the value of the header is non-empty. This is to circumvent issues where some common frameworks set this header even if no value is present (PR #1170)

diff:
@@ -24,7 +24,7 @@ use League\OAuth2\Server\Entities\RefreshTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
-use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
+use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
@@ -263,8 +263,9 @@ abstract class AbstractGrant implements GrantTypeInterface
ClientEntityInterface $client,
ServerRequestInterface $request
) {
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
if (!$validator->validateRedirectUri($redirectUri)) {
+ $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);
}
}

diff:
+<?php
+/**
+ * @author Sebastiano Degan <sebdeg87@gmail.com>
+ * @copyright Copyright (c) Sebastiano Degan
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+namespace League\OAuth2\Server\RedirectUriValidators;
+
+use League\OAuth2\Server\Entities\ClientEntityInterface;
+
+class RedirectUriValidator implements RedirectUriValidatorInterface
+{
+ /**
+ * @var ClientEntityInterface
+ */
+ private $client;
+
+ /**
+ * New validator instance for the given client
+ *
+ * @param ClientEntityInterface $client
+ */
+ public function __construct(ClientEntityInterface $client)
+ {
+ $this->client = $client;
+ }
+
+ /**
+ * Validates the redirect uri.
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if valid, false otherwise
+ */
+ public function validateRedirectUri($redirectUri)
+ {
+ $parsedUrl = $this->parseUrl($redirectUri);
+ if ($this->isLoopbackAddress($parsedUrl)) {
+ return $this->allowDifferentPort($parsedUrl);
+ } else {
+ return $this->matchExactUri($redirectUri);
+ }
+ }
+
+ /**
+ * Determine if the given url is a loopback url.
+ *
+ * @param array $parsedUri As returned by parseUrl
+ *
+ * @return bool
+ */
+ private function isLoopbackAddress(array $parsedUri)
+ {
+ return $parsedUri['scheme'] === 'http'
+ && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
+ }
+
+ /**
+ * Find an exact match among client uris
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if an exact match is found, false otherwise
+ */
+ private function matchExactUri($redirectUri)
+ {
+ return \in_array($redirectUri, $this->getClientRedirectUris(), true);
+ }
+
+ /**
+ * Find a match among client uris, allowing for different port numbers
+ *
+ * @param array $parsedUrl As returned by parseUrl
+ *
+ * @return bool Return true if a match is found, false otherwise
+ */
+ private function allowDifferentPort(array $parsedUrl)
+ {
+ foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
+ if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse an url like \parse_url, excluding the port
+ *
+ * @return array
+ */
+ private function parseUrl(string $url)
+ {
+ $parsedUrl = \parse_url($url);
+ unset($parsedUrl['port']);
+
+ return $parsedUrl;
+ }
+
+ /**
+ * Retrieve allowed client redirect uris
+ *
+ * @return array
+ */
+ private function getClientRedirectUris()
+ {
+ $clientRedirectUri = $this->client->getRedirectUri();
+ if (\is_string($clientRedirectUri)) {
+ return [$clientRedirectUri];
+ } elseif (\is_array($clientRedirectUri)) {
+ return $clientRedirectUri;
+ } else {
+ return [];
+ }
+ }
+}

diff:
+<?php
+/**
+ * @author Sebastiano Degan <sebdeg87@gmail.com>
+ * @copyright Copyright (c) Sebastiano Degan
+ * @license http://mit-license.org/
+ *
+ * @link https://github.com/thephpleague/oauth2-server
+ */
+
+namespace League\OAuth2\Server\RedirectUriValidators;
+
+interface RedirectUriValidatorInterface
+{
+ /**
+ * Validates the redirect uri.
+ *
+ * @param string $redirectUri
+ *
+ * @return bool Return true if valid, false otherwise
+ */
+ public function validateRedirectUri($redirectUri);
+}

change_type: DELETE

diff:
-<?php
-
-
-namespace League\OAuth2\Server\RedirectUriValidators;
-
-use League\OAuth2\Server\Entities\ClientEntityInterface;
-
-class Rfc8252RedirectUriValidator
-{
- private $client;
-
- public function __construct(ClientEntityInterface $client)
- {
- $this->client = $client;
- }
-
- public function validateRedirectUri(string $redirectUri)
- {
- $parsedUrl = $this->parseUrl($redirectUri);
- if ($this->isLoopbackAddress($parsedUrl)) {
- return $this->allowDifferentPort($parsedUrl);
- } else {
- return $this->matchFullUri($redirectUri);
- }
- }
-
- private function isLoopbackAddress(array $parsedUri)
- {
- return $parsedUri['scheme'] === 'http'
- && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
- }
-
- private function matchFullUri(string $redirectUri)
- {
- return \in_array($redirectUri, $this->getClientRedirectUris(), true);
- }
-
- private function allowDifferentPort(array $parsedUrl)
- {
- foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
- if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
- return true;
- }
- }
-
- return false;
- }
-
- private function parseUrl(string $url)
- {
- $parsedUrl = \parse_url($url);
- $parsedUrl['port'] = 80;
-
- return $parsedUrl;
- }
-
- private function getClientRedirectUris()
- {
- $clientRedirectUri = $this->client->getRedirectUri();
- if (\is_string($clientRedirectUri)) {
- return [$clientRedirectUri];
- } elseif (\is_array($clientRedirectUri)) {
- return $clientRedirectUri;
- } else {
- return [];
- }
- }
-}

change_type: RENAME

diff:
<?php
-
namespace LeagueTests\RedirectUriValidators;
-use League\OAuth2\Server\RedirectUriValidators\Rfc8252RedirectUriValidator;
+use League\OAuth2\Server\RedirectUriValidators\RedirectUriValidator;
use LeagueTests\Stubs\ClientEntity;
use PHPUnit\Framework\TestCase;
-class Rfc8252RedirectUriValidatorTest extends TestCase
+class RedirectUriValidatorTest extends TestCase
{
public function testInvalidNonLoopbackUri()
{
@@ -18,7 +17,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
]);
$redirectUri = 'https://example.com/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertFalse(
$validator->validateRedirectUri($redirectUri),
@@ -35,7 +34,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
]);
$redirectUri = 'https://example.com:8443/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),
@@ -50,7 +49,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://127.0.0.1:8443/different/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertFalse(
$validator->validateRedirectUri($redirectUri),
@@ -65,7 +64,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://127.0.0.1:8080/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),
@@ -80,7 +79,7 @@ class Rfc8252RedirectUriValidatorTest extends TestCase
$redirectUri = 'http://[::1]:8080/endpoint';
- $validator = new Rfc8252RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client);
$this->assertTrue(
$validator->validateRedirectUri($redirectUri),

original_message: fixed comments, names and changelog


message: removed scalar typehint and updated doc

diff:
@@ -91,9 +91,11 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
/**
* Parse an url like \parse_url, excluding the port
*
+ * @param string $url
+ *
* @return array
*/
- private function parseUrl(string $url)
+ private function parseUrl($url)
{
$parsedUrl = \parse_url($url);
unset($parsedUrl['port']);

original_message: removed scalar typehint and updated doc


message: clearer function name & doc

diff:
@@ -38,7 +38,7 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
public function validateRedirectUri($redirectUri)
{
$parsedUrl = $this->parseUrl($redirectUri);
- if ($this->isLoopbackAddress($parsedUrl)) {
+ if ($this->isLoopbackUri($parsedUrl)) {
return $this->allowDifferentPort($parsedUrl);
} else {
return $this->matchExactUri($redirectUri);
@@ -46,13 +46,15 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
}
/**
- * Determine if the given url is a loopback url.
+ * According to section 7.3 of rfc8252, loopback uris are:
+ * - "http://127.0.0.1:{port}/{path}" for IPv4
+ * - "http://[::1]:{port}/{path}" for IPv6
*
* @param array $parsedUri As returned by parseUrl
*
* @return bool
*/
- private function isLoopbackAddress(array $parsedUri)
+ private function isLoopbackUri(array $parsedUri)
{
return $parsedUri['scheme'] === 'http'
&& (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));

original_message: clearer function name & doc


message: removed unnecessary else blocks

diff:
@@ -40,9 +40,9 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
$parsedUrl = $this->parseUrl($redirectUri);
if ($this->isLoopbackUri($parsedUrl)) {
return $this->allowDifferentPort($parsedUrl);
- } else {
- return $this->matchExactUri($redirectUri);
}
+
+ return $this->matchExactUri($redirectUri);
}
/**
@@ -117,8 +117,8 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
return [$clientRedirectUri];
} elseif (\is_array($clientRedirectUri)) {
return $clientRedirectUri;
- } else {
- return [];
}
+
+ return [];
}
}

original_message: removed unnecessary else blocks


message: using redirectUri instead of client as constructor argument

diff:
@@ -263,7 +263,7 @@ abstract class AbstractGrant implements GrantTypeInterface
ClientEntityInterface $client,
ServerRequestInterface $request
) {
- $validator = new RedirectUriValidator($client);
+ $validator = new RedirectUriValidator($client->getRedirectUri());
if (!$validator->validateRedirectUri($redirectUri)) {
$this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
throw OAuthServerException::invalidClient($request);

diff:
@@ -14,18 +14,24 @@ use League\OAuth2\Server\Entities\ClientEntityInterface;
class RedirectUriValidator implements RedirectUriValidatorInterface
{
/**
- * @var ClientEntityInterface
+ * @var array
*/
- private $client;
+ private $allowedRedirectUris;
/**
- * New validator instance for the given client
+ * New validator instance for the given uri
*
- * @param ClientEntityInterface $client
+ * @param string|array $allowedRedirectUris
*/
- public function __construct(ClientEntityInterface $client)
+ public function __construct($allowedRedirectUri)
{
- $this->client = $client;
+ if (\is_string($allowedRedirectUri)) {
+ $this->allowedRedirectUris = [ $allowedRedirectUri ];
+ } elseif (\is_array($allowedRedirectUri)) {
+ $this->allowedRedirectUris = $allowedRedirectUri;
+ } else {
+ $this->allowedRedirectUris = [];
+ }
}
/**
@@ -69,7 +75,7 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
*/
private function matchExactUri($redirectUri)
{
- return \in_array($redirectUri, $this->getClientRedirectUris(), true);
+ return \in_array($redirectUri, $this->allowedRedirectUris, true);
}
/**
@@ -81,8 +87,8 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
*/
private function allowDifferentPort(array $parsedUrl)
{
- foreach ($this->getClientRedirectUris() as $clientRedirectUri) {
- if ($parsedUrl == $this->parseUrl($clientRedirectUri)) {
+ foreach ($this->allowedRedirectUris as $redirectUri) {
+ if ($parsedUrl == $this->parseUrl($redirectUri)) {
return true;
}
}
@@ -104,21 +110,4 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
return $parsedUrl;
}
-
- /**
- * Retrieve allowed client redirect uris
- *
- * @return array
- */
- private function getClientRedirectUris()
- {
- $clientRedirectUri = $this->client->getRedirectUri();
- if (\is_string($clientRedirectUri)) {
- return [$clientRedirectUri];
- } elseif (\is_array($clientRedirectUri)) {
- return $clientRedirectUri;
- }
-
- return [];
- }
}

diff:
@@ -10,79 +10,66 @@ class RedirectUriValidatorTest extends TestCase
{
public function testInvalidNonLoopbackUri()
{
- $client = new ClientEntity();
- $client->setRedirectUri([
+ $validator = new RedirectUriValidator([
'https://example.com:8443/endpoint',
'https://example.com/different/endpoint',
]);
- $redirectUri = 'https://example.com/endpoint';
- $validator = new RedirectUriValidator($client);
+ $invalidRedirectUri = 'https://example.com/endpoint';
$this->assertFalse(
- $validator->validateRedirectUri($redirectUri),
+ $validator->validateRedirectUri($invalidRedirectUri),
'Non loopback URI must match in every part'
);
}
public function testValidNonLoopbackUri()
{
- $client = new ClientEntity();
- $client->setRedirectUri([
+ $validator = new RedirectUriValidator([
'https://example.com:8443/endpoint',
'https://example.com/different/endpoint',
]);
- $redirectUri = 'https://example.com:8443/endpoint';
- $validator = new RedirectUriValidator($client);
+ $validRedirectUri = 'https://example.com:8443/endpoint';
$this->assertTrue(
- $validator->validateRedirectUri($redirectUri),
+ $validator->validateRedirectUri($validRedirectUri),
'Redirect URI must be valid when matching in every part'
);
}
public function testInvalidLoopbackUri()
{
- $client = new ClientEntity();
- $client->setRedirectUri('http://127.0.0.1:8443/endpoint');
+ $validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint');
- $redirectUri = 'http://127.0.0.1:8443/different/endpoint';
-
- $validator = new RedirectUriValidator($client);
+ $invalidRedirectUri = 'http://127.0.0.1:8443/different/endpoint';
$this->assertFalse(
- $validator->validateRedirectUri($redirectUri),
+ $validator->validateRedirectUri($invalidRedirectUri),
'Valid loopback redirect URI can change only the port number'
);
}
public function testValidLoopbackUri()
{
- $client = new ClientEntity();
- $client->setRedirectUri('http://127.0.0.1:8443/endpoint');
-
- $redirectUri = 'http://127.0.0.1:8080/endpoint';
+ $validator = new RedirectUriValidator('http://127.0.0.1:8443/endpoint');
- $validator = new RedirectUriValidator($client);
+ $validRedirectUri = 'http://127.0.0.1:8080/endpoint';
$this->assertTrue(
- $validator->validateRedirectUri($redirectUri),
+ $validator->validateRedirectUri($validRedirectUri),
'Loopback redirect URI can change the port number'
);
}
public function testValidIpv6LoopbackUri()
{
- $client = new ClientEntity();
- $client->setRedirectUri('http://[::1]:8443/endpoint');
-
- $redirectUri = 'http://[::1]:8080/endpoint';
+ $validator = new RedirectUriValidator('http://[::1]:8443/endpoint');
- $validator = new RedirectUriValidator($client);
+ $validRedirectUri = 'http://[::1]:8080/endpoint';
$this->assertTrue(
- $validator->validateRedirectUri($redirectUri),
+ $validator->validateRedirectUri($validRedirectUri),
'Loopback redirect URI can change the port number'
);
}


message: using strict array equality

diff:
@@ -88,7 +88,7 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
private function allowDifferentPort(array $parsedUrl)
{
foreach ($this->allowedRedirectUris as $redirectUri) {
- if ($parsedUrl == $this->parseUrl($redirectUri)) {
+ if ($parsedUrl === $this->parseUrl($redirectUri)) {
return true;
}
}

original_message: using strict array equality


message: fixed chagelog entry

diff:
@@ -5,7 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
-### Fixed
+### Added
The server will now validate redirect uris according to rfc8252 (PR #1203)
### Fixed

original_message: fixed chagelog entry


message: clearer function names

diff:
namespace League\OAuth2\Server\RedirectUriValidators;
-use League\OAuth2\Server\Entities\ClientEntityInterface;
-
class RedirectUriValidator implements RedirectUriValidatorInterface
{
/**
@@ -43,9 +41,9 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
*/
public function validateRedirectUri($redirectUri)
{
- $parsedUrl = $this->parseUrl($redirectUri);
+ $parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
if ($this->isLoopbackUri($parsedUrl)) {
- return $this->allowDifferentPort($parsedUrl);
+ return $this->matchUriExcludingPort($parsedUrl);
}
return $this->matchExactUri($redirectUri);
@@ -56,7 +54,7 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
* - "http://127.0.0.1:{port}/{path}" for IPv4
* - "http://[::1]:{port}/{path}" for IPv6
*
- * @param array $parsedUri As returned by parseUrl
+ * @param array $parsedUri As returned by parseUrlAndRemovePort
*
* @return bool
*/
@@ -81,14 +79,14 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
/**
* Find a match among client uris, allowing for different port numbers
*
- * @param array $parsedUrl As returned by parseUrl
+ * @param array $parsedUrl As returned by parseUrlAndRemovePort
*
* @return bool Return true if a match is found, false otherwise
*/
- private function allowDifferentPort(array $parsedUrl)
+ private function matchUriExcludingPort(array $parsedUrl)
{
foreach ($this->allowedRedirectUris as $redirectUri) {
- if ($parsedUrl === $this->parseUrl($redirectUri)) {
+ if ($parsedUrl === $this->parseUrlAndRemovePort($redirectUri)) {
return true;
}
}
@@ -103,7 +101,7 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
*
* @return array
*/
- private function parseUrl($url)
+ private function parseUrlAndRemovePort($url)
{
$parsedUrl = \parse_url($url);
unset($parsedUrl['port']);

original_message: clearer function names


message: minor refactoring to improve clarity

diff:
@@ -41,9 +41,8 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
*/
public function validateRedirectUri($redirectUri)
{
- $parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
- if ($this->isLoopbackUri($parsedUrl)) {
- return $this->matchUriExcludingPort($parsedUrl);
+ if ($this->isLoopbackUri($redirectUri)) {
+ return $this->matchUriExcludingPort($redirectUri);
}
return $this->matchExactUri($redirectUri);
@@ -54,18 +53,20 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
* - "http://127.0.0.1:{port}/{path}" for IPv4
* - "http://[::1]:{port}/{path}" for IPv6
*
- * @param array $parsedUri As returned by parseUrlAndRemovePort
+ * @param string $redirectUri
*
* @return bool
*/
- private function isLoopbackUri(array $parsedUri)
+ private function isLoopbackUri($redirectUri)
{
- return $parsedUri['scheme'] === 'http'
- && (\in_array($parsedUri['host'], ['127.0.0.1', '[::1]'], true));
+ $parsedUrl = parse_url($redirectUri);
+
+ return $parsedUrl['scheme'] === 'http'
+ && (\in_array($parsedUrl['host'], ['127.0.0.1', '[::1]'], true));
}
/**
- * Find an exact match among client uris
+ * Find an exact match among allowed uris
*
* @param string $redirectUri
*
@@ -77,14 +78,16 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
}
/**
- * Find a match among client uris, allowing for different port numbers
+ * Find a match among allowed uris, allowing for different port numbers
*
- * @param array $parsedUrl As returned by parseUrlAndRemovePort
+ * @param string $redirectUri
*
* @return bool Return true if a match is found, false otherwise
*/
- private function matchUriExcludingPort(array $parsedUrl)
+ private function matchUriExcludingPort($redirectUri)
{
+ $parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
+
foreach ($this->allowedRedirectUris as $redirectUri) {
if ($parsedUrl === $this->parseUrlAndRemovePort($redirectUri)) {
return true;

original_message: minor refactoring to improve clarity


message: fixed variable name conflict

diff:
@@ -88,8 +88,8 @@ class RedirectUriValidator implements RedirectUriValidatorInterface
{
$parsedUrl = $this->parseUrlAndRemovePort($redirectUri);
- foreach ($this->allowedRedirectUris as $redirectUri) {
- if ($parsedUrl === $this->parseUrlAndRemovePort($redirectUri)) {
+ foreach ($this->allowedRedirectUris as $allowedRedirectUri) {
+ if ($parsedUrl === $this->parseUrlAndRemovePort($allowedRedirectUri)) {
return true;
}
}

original_message: fixed variable name conflict


message: initial adjustments to change how camera works

old_path: .gitignore

new_path: .gitignore

diff:
@@ -73,3 +73,5 @@ build/
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+
+.vscode
\ No newline at end of file

diff:
@@ -35,7 +35,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.3.3"
+ version: "0.4.1"
boolean_selector:
dependency: transitive
description:
@@ -57,6 +57,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.3"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
collection:
dependency: transitive
description:
@@ -78,6 +85,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
flame:
dependency: transitive
description:
@@ -121,13 +135,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
- image:
- dependency: transitive
- description:
- name: image
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.12"
matcher:
dependency: transitive
description:
@@ -155,7 +162,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.6.4"
+ version: "1.7.0"
path_drawing:
dependency: transitive
description:
@@ -212,13 +219,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
- quiver:
- dependency: transitive
- description:
- name: quiver
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.3"
sky_engine:
dependency: transitive
description: flutter

diff:
@@ -61,7 +61,8 @@ abstract class JoystickController extends Component
void update(double t) {}
@override
- int priority() {
- return 25;
- }
+ int priority() => 25;
+
+ @override
+ bool isHud() => true;
}

diff:
@@ -11,6 +11,7 @@ import 'package:bonfire/util/game_intercafe/game_interface.dart';
import 'package:bonfire/util/lighting/lighting.dart';
import 'package:bonfire/util/map_explorer.dart';
import 'package:bonfire/util/value_generator.dart';
+import 'package:flame/components/component.dart';
import 'package:flame/keyboard.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -72,6 +73,19 @@ class RPGGame extends BaseGamePointerDetector with KeyboardEvents {
if (gameController != null) gameController.notifyListeners();
}
+ @override
+ void renderComponent(Canvas canvas, Component comp) {
+ if (!comp.loaded()) {
+ return;
+ }
+ canvas.save();
+ if (!comp.isHud()) {
+ canvas.translate(-gameCamera.position.x, -gameCamera.position.y);
+ }
+ comp.render(canvas);
+ canvas.restore();
+ }
+
void addEnemy(Enemy enemy) {
enemies.add(enemy);
add(enemy);

diff:
@@ -5,6 +5,10 @@ import 'package:bonfire/util/game_component.dart';
class BackgroundColorGame extends GameComponent {
final Color color;
BackgroundColorGame(this.color);
+
+ @override
+ bool isHud() => true;
+
@override
void render(Canvas canvas) {
canvas.drawRect(

diff:
@@ -7,6 +7,9 @@ import 'package:flutter/widgets.dart';
class GameInterface extends GameComponent {
List<InterfaceComponent> _components = List();
+ @override
+ bool isHud() => true;
+
@override
bool isTouchable = true;

diff:
@@ -10,6 +10,9 @@ class Lighting extends GameComponent {
Paint _paintFocus;
Iterable<LightingConfig> _lightToRender = List();
+ @override
+ bool isHud() => true;
+
Lighting({this.color = Colors.transparent}) {
_paintFocus = Paint()
..color = Colors.transparent
@@ -25,6 +28,8 @@ class Lighting extends GameComponent {
canvas.saveLayer(Offset.zero & size, Paint());
canvas.drawColor(color, BlendMode.dstATop);
_lightToRender.forEach((light) {
+ canvas.save();
+ canvas.translate(-gameRef.gameCamera.position.x, -gameRef.gameCamera.position.y);
canvas.drawCircle(
Offset(light.gameComponent.position.center.dx,
light.gameComponent.position.center.dy),
@@ -49,7 +54,9 @@ class Lighting extends GameComponent {
: 1),
paint,
);
+ canvas.restore();
});
+ canvas.restore();
}
static double convertRadiusToSigma(double radius) {

diff:
@@ -50,6 +50,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.3"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
collection:
dependency: transitive
description:
@@ -71,6 +78,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
flame:
dependency: "direct main"
description:
@@ -114,13 +128,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
- image:
- dependency: transitive
- description:
- name: image
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.12"
matcher:
dependency: transitive
description:
@@ -148,7 +155,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.6.4"
+ version: "1.7.0"
path_drawing:
dependency: transitive
description:
@@ -205,13 +212,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
- quiver:
- dependency: transitive
- description:
- name: quiver
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.3"
sky_engine:
dependency: transitive
description: flutter

repo: rafaelbarbosatec/bonfire


message: makes camera follow player

diff:
@@ -61,6 +61,8 @@ class Game extends StatelessWidget implements GameListener {
background: BackgroundColorGame(Colors.blueGrey[900]),
gameController: _controller..setListener(this),
lightingColorGame: Colors.black.withOpacity(0.4),
+ showCollisionArea: true,
+ constructionMode: true,
);
}

diff:
@@ -97,8 +97,8 @@ class Knight extends SimplePlayer with WithLighting {
gameRef.addDecoration(
GameDecoration(
initPosition: Position(
- positionInWorld.left,
- positionInWorld.top,
+ position.center.dx,
+ position.center.dy,
),
height: 30,
width: 30,
@@ -177,8 +177,9 @@ class Knight extends SimplePlayer with WithLighting {
@override
void update(double dt) {
- if (this.isDead) return;
+ if (this.isDead || gameRef?.size == null) return;
_verifyStamina();
+ this.gameRef.gameCamera.position = Position(position.center.dx - gameRef.size.width / 2, position.center.dy - gameRef.size.height / 2);
this.seeEnemy(
visionCells: 8,
notObserved: () {

diff:
@@ -27,7 +27,7 @@ class Player extends AnimatedObject
bool _isDead = false;
- bool _usePositionInWorld = true;
+ bool _usePositionInWorld = false;
bool _nextFrameUsePosition = false;
final Size sizeCentralMovementWindow;
@@ -73,75 +73,44 @@ class Player extends AnimatedObject
}
void moveTop(double speed) {
- if (position.top <= 0) return;
-
double innerSpeed = speed * _dtUpdate;
Rect displacement = position.translate(0, (innerSpeed * -1));
if (isCollision(displacement, gameRef)) return;
- if (position.top >= rectCentralMovementWindow.top ||
- gameRef.gameCamera.isMaxTop()) {
position = displacement;
- } else {
- gameRef.gameCamera
- .moveCamera(innerSpeed, JoystickMoveDirectional.MOVE_UP);
- }
}
void moveRight(double speed) {
- if (position.right >= gameRef.size.width) return;
-
double innerSpeed = speed * _dtUpdate;
Rect displacement = position.translate(innerSpeed, 0);
if (isCollision(displacement, gameRef)) return;
- if (position.right <= rectCentralMovementWindow.right ||
- gameRef.gameCamera.isMaxRight()) {
position = displacement;
- } else {
- gameRef.gameCamera
- .moveCamera(innerSpeed, JoystickMoveDirectional.MOVE_RIGHT);
- }
+
}
void moveBottom(double speed) {
- if (position.bottom >= gameRef.size.height) return;
-
double innerSpeed = speed * _dtUpdate;
Rect displacement = position.translate(0, innerSpeed);
if (isCollision(displacement, gameRef)) return;
- if (position.bottom <= rectCentralMovementWindow.bottom ||
- gameRef.gameCamera.isMaxBottom()) {
position = displacement;
- } else {
- gameRef.gameCamera
- .moveCamera(innerSpeed, JoystickMoveDirectional.MOVE_DOWN);
- }
}
void moveLeft(double speed) {
- if (position.left <= 0) return;
-
double innerSpeed = speed * _dtUpdate;
Rect displacement = position.translate(innerSpeed * -1, 0);
if (isCollision(displacement, gameRef)) return;
- if (position.left >= rectCentralMovementWindow.left ||
- gameRef.gameCamera.isMaxLeft()) {
position = displacement;
- } else {
- gameRef.gameCamera
- .moveCamera(innerSpeed, JoystickMoveDirectional.MOVE_LEFT);
- }
}
void moveFromAngle(double speed, double angle) {
@@ -255,7 +224,7 @@ class Player extends AnimatedObject
}
void usePositionInWorldToRender() {
- _usePositionInWorld = true;
+ _usePositionInWorld = false;
}
void usePositionToRender() {
@@ -264,7 +233,7 @@ class Player extends AnimatedObject
@override
get positionInWorld {
- if (_usePositionInWorld) return super.positionInWorld;
+ if (_usePositionInWorld) return super.position;
return super.positionInWorld = Rect.fromLTWH(
position.left - gameRef.gameCamera.position.x,

diff:
@@ -16,7 +16,7 @@ abstract class WorldComponent extends GameComponent {
@override
void update(double t) {
- position = positionInWordToPosition();
+ position ??= positionInWorld ?? Rect.zero;
}
@override
@@ -30,24 +30,16 @@ abstract class WorldComponent extends GameComponent {
}
bool isVisibleInMap() {
- if (gameRef == null || gameRef.size == null || position == null)
+ if (gameRef?.size == null || position == null || destroy() == true)
return false;
- return position.top < (gameRef.size.height + position.height) &&
- position.top > (position.height * -1) &&
- position.left > (position.width * -1) &&
- position.left < (gameRef.size.width + position.width) &&
- !destroy();
- }
-
- Rect positionInWordToPosition() {
- if (positionInWorld == null) return Rect.zero;
- if (gameRef == null) return positionInWorld;
- return Rect.fromLTWH(
- positionInWorld.left + gameRef.gameCamera.position.x,
- positionInWorld.top + gameRef.gameCamera.position.y,
- positionInWorld.width,
- positionInWorld.height,
+ final cameraRect = Rect.fromLTWH(
+ gameRef.gameCamera.position.x,
+ gameRef.gameCamera.position.y,
+ gameRef.size.width,
+ gameRef.size.height,
);
+
+ return position.overlaps(cameraRect);
}
}

original_message: makes camera follow player


message: remove player usePositionToRender

diff:
@@ -27,8 +27,6 @@ class Player extends AnimatedObject
bool _isDead = false;
- bool _nextFrameUsePosition = false;
-
final Size sizeCentralMovementWindow;
Rect rectCentralMovementWindow;
@@ -64,9 +62,7 @@ class Player extends AnimatedObject
@override
void update(double dt) {
super.update(dt);
- if (_nextFrameUsePosition) {
- _nextFrameUsePosition = false;
- }
+
_dtUpdate = dt;
}
@@ -221,10 +217,6 @@ class Player extends AnimatedObject
}
}
- void usePositionToRender() {
- _nextFrameUsePosition = true;
- }
-
Rect get rectCollision => getRectCollision(position);
@override

diff:
@@ -150,7 +150,6 @@ class Camera with HasGameRef<RPGGame> {
moveToPositionAnimated(
Position(_positionPlayer.left, _positionPlayer.top),
finish: () {
- gameRef.player.usePositionToRender();
if (finish != null) finish();
},
duration: duration,
@@ -161,7 +160,6 @@ class Camera with HasGameRef<RPGGame> {
if (gameRef.player == null) return;
Rect _positionPlayer = gameRef.player.position;
moveToPosition(Position(_positionPlayer.left, _positionPlayer.top));
- gameRef.player.usePositionToRender();
}
bool isComponentOnCamera(GameComponent c) {

original_message: remove player usePositionToRender


message: camera method to follow player

diff:
@@ -179,6 +179,7 @@ class Knight extends SimplePlayer with WithLighting {
void update(double dt) {
if (this.isDead || gameRef?.size == null) return;
_verifyStamina();
+ this.gameRef.gameCamera.moveToPlayer();
this.seeEnemy(
visionCells: 8,
notObserved: () {

diff:
@@ -156,10 +156,18 @@ class Camera with HasGameRef<RPGGame> {
);
}
- void moveToPlayer() {
- if (gameRef.player == null) return;
- Rect _positionPlayer = gameRef.player.position;
- moveToPosition(Position(_positionPlayer.left, _positionPlayer.top));
+ void moveToPlayer({double horizontal = 50, double vertical = 50}) {
+ if (gameRef?.player == null || gameRef?.size == null) return;
+ final screenCenter = Offset(gameRef.size.width / 2, gameRef.size.height / 2);
+ final positionPlayer = worldPositionToScreen(gameRef.player.position.center);
+ final horizontalDistance = screenCenter.dx - positionPlayer.dx;
+ final verticalDistance = screenCenter.dy - positionPlayer.dy;
+ if (horizontalDistance.abs() > horizontal) {
+ this.gameRef.gameCamera.position.x += horizontalDistance > 0 ? horizontal - horizontalDistance : -horizontalDistance - horizontal;
+ }
+ if (verticalDistance.abs() > vertical) {
+ this.gameRef.gameCamera.position.y += verticalDistance > 0 ? vertical - verticalDistance : -verticalDistance - vertical;
+ }
}
bool isComponentOnCamera(GameComponent c) {
@@ -167,4 +175,8 @@ class Camera with HasGameRef<RPGGame> {
return cameraRect.overlaps(c.position);
}
+
+ Offset worldPositionToScreen(Offset position) {
+ return position.translate(-gameRef.gameCamera.position.x, -gameRef.gameCamera.position.y);
+ }
}

diff:
@@ -28,13 +28,6 @@ abstract class WorldComponent extends GameComponent {
if (gameRef?.size == null || position == null || destroy() == true)
return false;
- final cameraRect = Rect.fromLTWH(
- gameRef.gameCamera.position.x,
- gameRef.gameCamera.position.y,
- gameRef.size.width,
- gameRef.size.height,
- );
-
- return position.overlaps(cameraRect);
+ return position.overlaps(gameRef.gameCamera.cameraRect);
}
}

original_message: camera method to follow player


message: make camera start on player and remove camera max methods

diff:
@@ -99,7 +99,7 @@ class MapWorld extends MapGame {
lastCameraX = -1;
lastCameraY = -1;
- gameRef.gameCamera.moveToPlayer();
+ gameRef.gameCamera.moveToPlayer(horizontal: 0, vertical: 0);
}
@override

diff:
@@ -120,25 +120,21 @@ class Player extends AnimatedObject
bool enableAxisY = false;
if (diffBase.dx > 0) {
- if (position.right <= rectCentralMovementWindow.right ||
- gameRef.gameCamera.isMaxRight()) {
+ if (position.right <= rectCentralMovementWindow.right) {
enableAxisX = true;
}
} else if (diffBase.dx < 0) {
- if (position.left >= rectCentralMovementWindow.left ||
- gameRef.gameCamera.isMaxLeft()) {
+ if (position.left >= rectCentralMovementWindow.left) {
enableAxisX = true;
}
}
if (diffBase.dy > 0) {
- if (position.bottom <= rectCentralMovementWindow.bottom ||
- gameRef.gameCamera.isMaxBottom()) {
+ if (position.bottom <= rectCentralMovementWindow.bottom) {
enableAxisY = true;
}
} else if (diffBase.dy < 0) {
- if (position.top >= rectCentralMovementWindow.top ||
- gameRef.gameCamera.isMaxTop()) {
+ if (position.top >= rectCentralMovementWindow.top) {
enableAxisY = true;
}
}

diff:
@@ -11,67 +11,35 @@ class Camera with HasGameRef<RPGGame> {
Rect get cameraRect => Rect.fromLTWH(position.x, position.y, gameRef.size.width, gameRef.size.height);
- bool isMaxBottom() {
- return (position.y * -1) >= maxTop;
- }
-
- bool isMaxLeft() {
- return position.x == 0;
- }
-
- bool isMaxRight() {
- return (position.x * -1) >= maxLeft;
- }
-
- bool isMaxTop() {
- return position.y == 0;
- }
-
void moveTop(double displacement) {
- if (position.y < 0) {
- position.y = position.y + displacement;
- }
- if (position.y > 0) {
- position.y = 0;
- }
+ position.y = position.y - displacement;
}
void moveRight(double displacement) {
- if (!isMaxRight()) {
- gameRef.gameCamera.position.x =
- gameRef.gameCamera.position.x - displacement;
- }
+ position.x = position.x + displacement;
}
void moveBottom(double displacement) {
- if (!isMaxBottom()) {
- gameRef.gameCamera.position.y =
- gameRef.gameCamera.position.y - displacement;
- }
+ position.y = position.y + displacement;
}
void moveLeft(double displacement) {
- if (!isMaxLeft()) {
- position.x = position.x + displacement;
- }
- if (position.x > 0) {
- position.x = 0;
- }
+ position.x = position.x - displacement;
}
void moveCamera(double displacement, JoystickMoveDirectional directional) {
switch (directional) {
case JoystickMoveDirectional.MOVE_UP:
- gameRef.gameCamera.moveTop(displacement);
+ moveTop(displacement);
break;
case JoystickMoveDirectional.MOVE_RIGHT:
- gameRef.gameCamera.moveRight(displacement);
+ moveRight(displacement);
break;
case JoystickMoveDirectional.MOVE_DOWN:
- gameRef.gameCamera.moveBottom(displacement);
+ moveBottom(displacement);
break;
case JoystickMoveDirectional.MOVE_LEFT:
- gameRef.gameCamera.moveLeft(displacement);
+ moveLeft(displacement);
break;
case JoystickMoveDirectional.MOVE_UP_LEFT:
break;
@@ -125,23 +93,10 @@ class Camera with HasGameRef<RPGGame> {
}
void moveToPosition(Position position) {
- double distanceLeft = gameRef.size.width / 2;
- double distanceTop = gameRef.size.height / 2;
-
- double positionLeftCamera = position.x - distanceLeft;
- double positionTopCamera = position.y - distanceTop;
-
- if (positionLeftCamera > maxLeft) positionLeftCamera = maxLeft;
-
- positionLeftCamera *= -1;
- if (positionLeftCamera > 0) positionLeftCamera = 0;
-
- if (positionTopCamera * -1 > maxTop) positionTopCamera = maxTop;
- positionTopCamera *= -1;
- if (positionTopCamera > 0) positionTopCamera = 0;
+ if (gameRef?.size == null) return;
+ final screenCenter = Position(gameRef.size.width / 2, gameRef.size.height / 2);
- this.position.x = positionLeftCamera;
- this.position.y = positionTopCamera;
+ this.position = position - screenCenter;
}
void moveToPlayerAnimated({Duration duration, VoidCallback finish}) {
@@ -157,7 +112,7 @@ class Camera with HasGameRef<RPGGame> {
}
void moveToPlayer({double horizontal = 50, double vertical = 50}) {
- if (gameRef?.player == null || gameRef?.size == null) return;
+ if (gameRef?.player == null || gameRef?.size == null) return print("but null...");
final screenCenter = Offset(gameRef.size.width / 2, gameRef.size.height / 2);
final positionPlayer = worldPositionToScreen(gameRef.player.position.center);
final horizontalDistance = screenCenter.dx - positionPlayer.dx;


message: remove map_world maxTop maxLeft

diff:
@@ -74,27 +74,6 @@ class MapWorld extends MapGame {
void verifyMaxTopAndLeft(Size size) {
if (lastSize == size) return;
lastSize = size;
- double maxTop = 0;
- double maxLeft = 0;
- maxTop = tiles.fold(0, (max, tile) {
- if (tile.position.bottom > max)
- return tile.position.bottom;
- else
- return max;
- });
-
- maxTop -= size.height;
-
- maxLeft = tiles.fold(0, (max, tile) {
- if (tile.position.right > max)
- return tile.position.right;
- else
- return max;
- });
- maxLeft -= size.width;
-
- gameRef.gameCamera.maxLeft = maxLeft;
- gameRef.gameCamera.maxTop = maxTop;
lastCameraX = -1;
lastCameraY = -1;

original_message: remove map_world maxTop maxLeft


message: fix moveToPositionAnimated

diff:
@@ -60,23 +60,10 @@ class Camera with HasGameRef<RPGGame> {
Duration duration,
Curve curve = Curves.decelerate,
}) {
- double distanceLeft = gameRef.size.width / 2;
- double distanceTop = gameRef.size.height / 2;
-
- double positionLeftCamera = position.x - distanceLeft;
- double positionTopCamera = position.y - distanceTop;
-
- if (positionLeftCamera > maxLeft) positionLeftCamera = maxLeft;
-
- positionLeftCamera *= -1;
- if (positionLeftCamera > 0) positionLeftCamera = 0;
-
- if (positionTopCamera * -1 > maxTop) positionTopCamera = maxTop;
- positionTopCamera *= -1;
- if (positionTopCamera > 0) positionTopCamera = 0;
+ final screenCenter = Position(gameRef.size.width / 2, gameRef.size.height / 2);
- double diffX = this.position.x - positionLeftCamera;
- double diffY = this.position.y - positionTopCamera;
+ double diffX = this.position.x - (position.x - screenCenter.x);
+ double diffY = this.position.y - (position.y - screenCenter.y);
double originX = this.position.x;
double originY = this.position.y;

original_message: fix moveToPositionAnimated


message: fix moveToPlayerAnimated

diff:
@@ -72,9 +72,7 @@ class Camera with HasGameRef<RPGGame> {
this.position.x = originX - (diffX * value);
this.position.y = originY - (diffY * value);
})
- ..addListenerFinish(() {
- if (finish != null) finish();
- })
+ ..addListenerFinish(finish)
..addCurve(curve)
..start();
}
@@ -88,12 +86,10 @@ class Camera with HasGameRef<RPGGame> {
void moveToPlayerAnimated({Duration duration, VoidCallback finish}) {
if (gameRef.player == null) return;
- Rect _positionPlayer = gameRef.player.position;
+ final _positionPlayer = gameRef.player.position;
moveToPositionAnimated(
- Position(_positionPlayer.left, _positionPlayer.top),
- finish: () {
- if (finish != null) finish();
- },
+ Position(_positionPlayer.center.dx, _positionPlayer.center.dy),
+ finish: finish,
duration: duration,
);
}

original_message: fix moveToPlayerAnimated


message: fix typo on folder name

diff:
@@ -27,9 +27,9 @@ export 'package:bonfire/util/collision/collision.dart';
export 'package:bonfire/util/direction.dart';
export 'package:bonfire/util/functions.dart';
export 'package:bonfire/util/game_controller.dart';
-export 'package:bonfire/util/game_intercafe/game_interface.dart';
-export 'package:bonfire/util/game_intercafe/interface_component.dart';
-export 'package:bonfire/util/game_intercafe/text_interface_component.dart';
+export 'package:bonfire/util/game_interface/game_interface.dart';
+export 'package:bonfire/util/game_interface/interface_component.dart';
+export 'package:bonfire/util/game_interface/text_interface_component.dart';
export 'package:bonfire/util/lighting/lighting_config.dart';
export 'package:bonfire/util/lighting/with_lighting.dart';
export 'package:bonfire/util/objects/animated_follower_object.dart';

diff:
@@ -6,7 +6,7 @@ import 'package:bonfire/player/player.dart';
import 'package:bonfire/rpg_game.dart';
import 'package:bonfire/util/game_component.dart';
import 'package:bonfire/util/game_controller.dart';
-import 'package:bonfire/util/game_intercafe/game_interface.dart';
+import 'package:bonfire/util/game_interface/game_interface.dart';
import 'package:flutter/material.dart';
class BonfireWidget extends StatefulWidget {

diff:
@@ -7,7 +7,7 @@ import 'package:bonfire/util/base_game_point_detector.dart';
import 'package:bonfire/util/camera.dart';
import 'package:bonfire/util/game_component.dart';
import 'package:bonfire/util/game_controller.dart';
-import 'package:bonfire/util/game_intercafe/game_interface.dart';
+import 'package:bonfire/util/game_interface/game_interface.dart';
import 'package:bonfire/util/interval_tick.dart';
import 'package:bonfire/util/lighting/lighting.dart';
import 'package:bonfire/util/map_explorer.dart';

diff:
import 'dart:ui';
import 'package:bonfire/util/game_component.dart';
-import 'package:bonfire/util/game_intercafe/interface_component.dart';
+import 'package:bonfire/util/game_interface/interface_component.dart';
import 'package:flutter/widgets.dart';
class GameInterface extends GameComponent {

original_message: fix typo on folder name


message: fix verifyExistiInWorld

diff:
+import 'dart:ui';
import 'package:bonfire/map/tile.dart';
import 'package:bonfire/rpg_game.dart';
import 'package:flame/components/component.dart';
@@ -14,4 +15,6 @@ abstract class MapGame extends Component with HasGameRef<RPGGame> {
Iterable<Tile> getCollisions();
void updateTiles(Iterable<Tile> map);
+
+ Size getMapSize();
}

diff:
@@ -97,4 +97,17 @@ class MapWorld extends MapGame {
this.tiles = map;
verifyMaxTopAndLeft(gameRef.size);
}
+
+ @override
+ Size getMapSize() {
+ double height = 0;
+ double width = 0;
+
+ this.tiles.forEach((tile) {
+ if (tile.position.right > width) width = tile.position.right;
+ if (tile.position.bottom > height) height = tile.position.bottom;
+ });
+
+ return Size(width, height);
+ }
}

diff:
@@ -149,21 +149,19 @@ class FlyingAttackAngleObject extends AnimatedObject
}
bool _verifyExistInWorld() {
- // TODO: Update this logic
+ final mapSize = gameRef.map.getMapSize();
bool result = true;
if (position.left < 0) {
result = false;
}
- if (position.right >
- gameRef.gameCamera.maxLeft + gameRef.size.width) {
+ if (position.right > mapSize.width) {
result = false;
}
if (position.top < 0) {
result = false;
}
- if (position.bottom >
- gameRef.gameCamera.maxTop + gameRef.size.height) {
+ if (position.bottom > mapSize.height) {
result = false;
}

original_message: fix verifyExistiInWorld


message: allow decorations to be only sensor

diff:
@@ -28,17 +28,22 @@ class GameDecoration extends AnimatedObject with ObjectCollision {
/// World position that this decoration must position yourself.
final Position initPosition;
+ /// If set to true, won't be triggered on collision, but will trigger onContact()
+ final bool isSensor;
+
Sprite _sprite;
- GameDecoration(
- {Sprite sprite,
+ GameDecoration({
+ Sprite sprite,
@required this.initPosition,
@required this.height,
@required this.width,
this.frontFromPlayer = false,
+ this.isSensor = false,
FlameAnimation.Animation animation,
Collision collision,
- bool isTouchable = false}) {
+ bool isTouchable = false,
+ }) {
this.animation = animation;
_sprite = sprite;
this.position = generateRectWithBleedingPixel(
@@ -56,6 +61,7 @@ class GameDecoration extends AnimatedObject with ObjectCollision {
@required this.height,
@required this.width,
this.frontFromPlayer = false,
+ this.isSensor = false,
Collision collision,
bool isTouchable = false,
}) {
@@ -74,6 +80,7 @@ class GameDecoration extends AnimatedObject with ObjectCollision {
@required this.initPosition,
@required this.height,
@required this.width,
+ this.isSensor = false,
this.frontFromPlayer = false,
Collision collision,
bool isTouchable = false,
@@ -107,6 +114,8 @@ class GameDecoration extends AnimatedObject with ObjectCollision {
}
}
+ void onContact(ObjectCollision collision) { }
+
Rect generateRectWithBleedingPixel(
Position position,
double width,

diff:
@@ -73,6 +73,8 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
+ triggerSensors(displacement, gameRef);
+
position = displacement;
}
@@ -83,6 +85,8 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
+ triggerSensors(displacement, gameRef);
+
position = displacement;
}
@@ -94,6 +98,8 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
+ triggerSensors(displacement, gameRef);
+
position = displacement;
}
@@ -104,6 +110,8 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
+ triggerSensors(displacement, gameRef);
+
position = displacement;
}
@@ -150,6 +158,7 @@ class Player extends AnimatedObject
Rect newPosition = position.shift(newDiffBase);
if (isCollision(newPosition, gameRef)) return;
+ triggerSensors(newPosition, gameRef);
position = newPosition;

diff:
@@ -8,10 +8,22 @@ import 'package:flutter/material.dart';
mixin ObjectCollision {
Collision collision = Collision();
+ void triggerSensors(Rect displacement, RPGGame game) {
+ Rect rectCollision = getRectCollision(displacement);
+
+ final sensors = game.visibleDecorations().where(
+ (decoration) => decoration.isSensor,
+ );
+
+ sensors.forEach((decoration) {
+ if (decoration.rectCollision.overlaps(rectCollision)) decoration.onContact(this);
+ });
+ }
+
bool isCollision(Rect displacement, RPGGame game, {bool onlyVisible = true}) {
Rect rectCollision = getRectCollision(displacement);
- var collisions = (onlyVisible
+ final collisions = (onlyVisible
? game.map.getCollisionsRendered()
: game.map.getCollisions())
.where((i) => i.position.overlaps(rectCollision));
@@ -20,11 +32,12 @@ mixin ObjectCollision {
return true;
}
- var collisionsDecorations =
- (onlyVisible ? game.visibleDecorations() : game.decorations())
- .where((i) =>
- i.collision != null && i.rectCollision.overlaps(rectCollision))
- .cast();
+ final collisionsDecorations =
+ (onlyVisible ? game.visibleDecorations() : game.decorations()).where(
+ (i) =>
+ !i.isSensor &&
+ i.collision != null &&
+ i.rectCollision.overlaps(rectCollision));
if (collisionsDecorations.length > 0) {
return true;

original_message: allow decorations to be only sensor


message: fix game interface name

diff:
import 'dart:ui';
import 'package:bonfire/bonfire.dart';
+import 'package:bonfire/util/game_interface/interface_component.dart';
import 'package:example/player/knight.dart';
import 'package:flutter/material.dart';

diff:
import 'dart:ui';
import 'package:bonfire/bonfire.dart';
+import 'package:bonfire/util/game_interface/game_interface.dart';
+import 'package:bonfire/util/game_interface/interface_component.dart';
import 'package:example/interface/bar_life_component.dart';
import 'package:example/player/knight.dart';
import 'package:flutter/material.dart';

diff:
import 'dart:ui';
import 'package:bonfire/bonfire.dart';
+import 'package:bonfire/util/game_interface/interface_component.dart';
import 'package:flame/text_config.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

original_message: fix game interface name


message: check light visibility

diff:
@@ -162,7 +162,7 @@ class RPGGame extends BaseGamePointerDetector with KeyboardEvents {
_lightToRender = components
.where((element) =>
element is WithLighting &&
- (element as WithLighting).isVisible(size))
+ (element as WithLighting).isVisible(gameCamera))
.map((e) => (e as WithLighting).lightingConfig);
}

diff:
+import 'package:bonfire/util/camera.dart';
import 'package:bonfire/util/lighting/lighting_config.dart';
import 'package:flutter/material.dart';
mixin WithLighting {
LightingConfig lightingConfig;
- bool isVisible(Size sizeScreen) {
+ bool isVisible(Camera camera) {
if (lightingConfig == null ||
- sizeScreen == null ||
+ camera == null ||
lightingConfig?.gameComponent?.position == null) return false;
Rect rectLight = Rect.fromLTWH(
@@ -16,9 +17,6 @@ mixin WithLighting {
lightingConfig.radius * 2,
);
- return rectLight.top < (sizeScreen.height + rectLight.height) &&
- rectLight.top > (rectLight.height * -1) &&
- rectLight.left > (rectLight.width * -1) &&
- rectLight.left < (sizeScreen.width + rectLight.width);
+ return camera.cameraRect.overlaps(rectLight) ?? false;
}
}

original_message: check light visibility


message: spike example and trigger sensor on isCollision

diff: Binary files /dev/null and b/example/assets/images/itens/spikes.png differ

diff:
+import 'package:bonfire/bonfire.dart';
+import 'package:example/map/dungeon_map.dart';
+import 'package:flame/position.dart';
+
+const TIME_BETWEEN_HITS = 1.5;
+
+class Spikes extends GameDecoration {
+ final Position initPosition;
+ double _timeSinceLastHit = 0;
+
+ Spikes(this.initPosition)
+ : super.sprite(
+ Sprite('itens/spikes.png'),
+ initPosition: initPosition,
+ width: DungeonMap.tileSize / 1.5,
+ height: DungeonMap.tileSize / 1.5,
+ isSensor: true,
+ collision: Collision(
+ width: DungeonMap.tileSize / 2,
+ height: DungeonMap.tileSize / 2,
+ align: CollisionAlign.CENTER,
+ ),
+ );
+
+ @override
+ void update(double dt) {
+ if (_timeSinceLastHit > 0) {
+ _timeSinceLastHit -= dt;
+ }
+ }
+
+ @override
+ void onContact(collision) {
+ // only allows hit player after 1.5s from last hit
+ if (_timeSinceLastHit > 0) return;
+
+ // only triggers on collision with player
+ if (collision.runtimeType.toString() == 'Knight') {
+ _timeSinceLastHit = TIME_BETWEEN_HITS;
+ gameRef.player.receiveDamage(10, 1);
+ }
+ }
+}

diff:
@@ -61,6 +61,8 @@ class Game extends StatelessWidget implements GameListener {
background: BackgroundColorGame(Colors.blueGrey[900]),
gameController: _controller..setListener(this),
lightingColorGame: Colors.black.withOpacity(0.5),
+ constructionMode: true,
+ showCollisionArea: true,
);
}

diff:
@@ -2,6 +2,7 @@ import 'dart:math';
import 'package:bonfire/bonfire.dart';
import 'package:example/decoration/chest.dart';
+import 'package:example/decoration/spikes.dart';
import 'package:example/decoration/torch.dart';
import 'package:example/enemy/goblin.dart';
import 'package:flame/position.dart';
@@ -74,6 +75,9 @@ class DungeonMap {
static List<GameDecoration> decorations() {
return [
+ Spikes(
+ getRelativeTilePosition(7, 7),
+ ),
GameDecoration.sprite(
Sprite('itens/barrel.png'),
initPosition: getRelativeTilePosition(10, 6),

diff:
@@ -74,8 +74,6 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
- triggerSensors(displacement, gameRef);
-
position = displacement;
}
@@ -86,8 +84,6 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
- triggerSensors(displacement, gameRef);
-
position = displacement;
}
@@ -99,8 +95,6 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
- triggerSensors(displacement, gameRef);
-
position = displacement;
}
@@ -111,8 +105,6 @@ class Player extends AnimatedObject
if (isCollision(displacement, gameRef)) return;
- triggerSensors(displacement, gameRef);
-
position = displacement;
}

diff:
@@ -16,12 +16,19 @@ mixin ObjectCollision {
);
sensors.forEach((decoration) {
- if (decoration.rectCollision.overlaps(rectCollision)) decoration.onContact(this);
+ if (decoration.rectCollision.overlaps(rectCollision))
+ decoration.onContact(this);
});
}
- bool isCollision(Rect displacement, RPGGame game, {bool onlyVisible = true}) {
+ bool isCollision(
+ Rect displacement,
+ RPGGame game, {
+ bool onlyVisible = true,
+ bool shouldTriggerSensors = true,
+ }) {
Rect rectCollision = getRectCollision(displacement);
+ if (shouldTriggerSensors) triggerSensors(displacement, game);
final collisions = (onlyVisible
? game.map.getCollisionsRendered()


message: remove collision and grid from example

diff:
@@ -61,8 +61,6 @@ class Game extends StatelessWidget implements GameListener {
background: BackgroundColorGame(Colors.blueGrey[900]),
gameController: _controller..setListener(this),
lightingColorGame: Colors.black.withOpacity(0.5),
- constructionMode: true,
- showCollisionArea: true,
);
}

original_message: remove collision and grid from example


message: improve camera performance

diff:
@@ -4,7 +4,6 @@ import 'package:bonfire/joystick/joystick_controller.dart';
import 'package:bonfire/map/map_game.dart';
import 'package:bonfire/player/player.dart';
import 'package:bonfire/util/base_game_point_detector.dart';
-import 'package:bonfire/util/camera.dart';
import 'package:bonfire/util/game_component.dart';
import 'package:bonfire/util/game_controller.dart';
import 'package:bonfire/util/game_interface/game_interface.dart';
@@ -14,7 +13,6 @@ import 'package:bonfire/util/lighting/lighting_config.dart';
import 'package:bonfire/util/lighting/with_lighting.dart';
import 'package:bonfire/util/map_explorer.dart';
import 'package:bonfire/util/value_generator.dart';
-import 'package:flame/components/component.dart';
import 'package:flame/keyboard.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -28,7 +26,6 @@ class RPGGame extends BaseGamePointerDetector with KeyboardEvents {
final MapGame map;
final JoystickController joystickController;
final GameComponent background;
- final Camera gameCamera = Camera();
final bool constructionMode;
final bool showCollisionArea;
final GameController gameController;
@@ -83,19 +80,6 @@ class RPGGame extends BaseGamePointerDetector with KeyboardEvents {
super.update(t);
}
- @override
- void renderComponent(Canvas canvas, Component comp) {
- if (!comp.loaded()) {
- return;
- }
- canvas.save();
- if (!comp.isHud()) {
- canvas.translate(-gameCamera.position.x, -gameCamera.position.y);
- }
- comp.render(canvas);
- canvas.restore();
- }
-
void addEnemy(Enemy enemy) {
add(enemy);
}

diff:
@@ -9,9 +9,11 @@ import 'package:flame/game/game.dart';
import 'package:flutter/widgets.dart';
import 'package:ordered_set/comparing.dart';
import 'package:ordered_set/ordered_set.dart';
+import 'package:bonfire/util/camera.dart';
abstract class BaseGamePointerDetector extends Game with PointerDetector {
bool _isPause = false;
+ final Camera gameCamera = Camera();
/// The list of components to be updated and rendered by the base game.
OrderedSet<Component> components =
@@ -111,7 +113,10 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
@override
void render(Canvas canvas) {
canvas.save();
+
+ canvas.translate(-gameCamera.position.x, -gameCamera.position.y);
components.forEach((comp) => renderComponent(canvas, comp));
+
canvas.restore();
}
@@ -119,13 +124,18 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
///
/// It translates the camera unless hud, call the render method and restore the canvas.
/// This makes sure the canvas is not messed up by one component and all components render independently.
- void renderComponent(Canvas canvas, Component c) {
- if (!c.loaded()) {
+ void renderComponent(Canvas canvas, Component comp) {
+ if (!comp.loaded()) {
return;
}
- c.render(canvas);
- canvas.restore();
canvas.save();
+
+ if (comp.isHud()) {
+ canvas.translate(gameCamera.position.x, gameCamera.position.y);
+ }
+ comp.render(canvas);
+
+ canvas.restore();
}
/// This implementation of update updates every component in the list.
@@ -179,7 +189,7 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
/// This is a hook that comes from the RenderBox to allow recording of render times and statistics.
@override
void recordDt(double dt) {
- if (debugMode()) {
+ if (recordFps()) {
_dts.add(dt);
}
}

original_message: improve camera performance


message: only render components on screen

diff:
@@ -127,6 +127,8 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
void renderComponent(Canvas canvas, Component comp) {
if (!comp.loaded()) {
return;
+ } else if (comp is GameComponent) {
+ if (!comp.isHud() && !gameCamera.isComponentOnCamera(comp)) return;
}
canvas.save();

original_message: only render components on screen


message: fix verify exists in world

diff:
@@ -152,20 +152,20 @@ class FlyingAttackAngleObject extends AnimatedObject
}
bool _verifyExistInWorld() {
- bool result = true;
Size mapSize = gameRef.map.mapSize;
- if (position.left < gameRef.gameCamera.position.x) {
- result = false;
+ if (position.left < 0) {
+ return false;
}
- if (position.right > gameRef.gameCamera.position.x + mapSize.width) {
- result = false;
+ if (position.right > mapSize.width) {
+ return false;
}
- if (position.top < gameRef.gameCamera.position.y) {
- result = false;
+ if (position.top < 0) {
+ return false;
}
- if (position.bottom > gameRef.gameCamera.position.y + mapSize.height) {
- result = false;
+ if (position.bottom > mapSize.height) {
+ return false;
}
- return result;
+
+ return true;
}
}

diff:
@@ -167,22 +167,20 @@ class FlyingAttackObject extends AnimatedObject
}
bool _verifyExistInWorld() {
- bool result = true;
- if (position.left < gameRef.gameCamera.position.x) {
- result = false;
+ Size mapSize = gameRef.map.mapSize;
+ if (position.left < 0) {
+ return false;
}
- if (position.right >
- gameRef.gameCamera.position.x + gameRef.map.mapSize.width) {
- result = false;
+ if (position.right > mapSize.width) {
+ return false;
}
- if (position.top < gameRef.gameCamera.position.y) {
- result = false;
+ if (position.top < 0) {
+ return false;
}
- if (position.bottom >
- gameRef.gameCamera.position.y + gameRef.map.mapSize.height) {
- result = false;
+ if (position.bottom > mapSize.height) {
+ return false;
}
- return result;
+ return true;
}
}

original_message: fix verify exists in world


message: allow camera zoom

diff:
@@ -62,6 +62,7 @@ class Game extends StatelessWidget implements GameListener {
gameController: _controller..setListener(this),
lightingColorGame: Colors.black.withOpacity(0.5),
showFPS: true,
+ zoom: 1, // you can change this to change the game zoom or directly on the camera
);
}

diff:
@@ -24,6 +24,7 @@ class BonfireWidget extends StatefulWidget {
final Color constructionModeColor;
final Color collisionAreaColor;
final Color lightingColorGame;
+ final double zoom;
const BonfireWidget({
Key key,
@@ -41,6 +42,7 @@ class BonfireWidget extends StatefulWidget {
this.constructionModeColor,
this.collisionAreaColor,
this.lightingColorGame,
+ this.zoom,
}) : super(key: key);
@override
@@ -88,6 +90,7 @@ class _BonfireWidgetState extends State<BonfireWidget>
collisionAreaColor:
widget.collisionAreaColor ?? Colors.lightGreenAccent.withOpacity(0.5),
lightingColorGame: widget.lightingColorGame,
+ zoom: widget.zoom,
);
super.initState();
}

diff:
@@ -4,6 +4,7 @@ import 'package:bonfire/joystick/joystick_controller.dart';
import 'package:bonfire/map/map_game.dart';
import 'package:bonfire/player/player.dart';
import 'package:bonfire/util/base_game_point_detector.dart';
+import 'package:bonfire/util/camera.dart';
import 'package:bonfire/util/game_component.dart';
import 'package:bonfire/util/game_controller.dart';
import 'package:bonfire/util/game_interface/game_interface.dart';
@@ -59,9 +60,11 @@ class RPGGame extends BaseGamePointerDetector with KeyboardEvents {
this.constructionModeColor,
this.collisionAreaColor,
this.lightingColorGame,
+ double zoom,
}) : assert(map != null),
assert(context != null),
assert(joystickController != null) {
+ if (zoom != null) gameCamera = Camera(zoom: zoom);
gameCamera.gameRef = this;
joystickController.joystickListener = player ?? MapExplorer(gameCamera);
if (gameController != null) gameController.setGame(this);

diff:
@@ -15,7 +15,7 @@ import 'package:ordered_set/ordered_set.dart';
abstract class BaseGamePointerDetector extends Game with PointerDetector {
bool _isPause = false;
final CustomWidgetBuilder widgetBuilder = CustomWidgetBuilder();
- final Camera gameCamera = Camera();
+ Camera gameCamera = Camera();
/// The list of components to be updated and rendered by the base game.
OrderedSet<Component> components =
@@ -115,7 +115,11 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
@override
void render(Canvas canvas) {
canvas.save();
+
+ canvas.translate(size.width/2, size.height/2);
+ canvas.scale(gameCamera.zoom);
canvas.translate(-gameCamera.position.x, -gameCamera.position.y);
+
components.forEach((comp) => renderComponent(canvas, comp));
canvas.restore();
}
@@ -130,14 +134,16 @@ abstract class BaseGamePointerDetector extends Game with PointerDetector {
} else if (comp is GameComponent) {
if (!comp.isHud() && !comp.isVisibleInCamera()) return;
}
+ canvas.save();
if (comp.isHud()) {
- canvas.save();
canvas.translate(gameCamera.position.x, gameCamera.position.y);
+ canvas.scale(1/gameCamera.zoom);
+ canvas.translate(-size.width/2, -size.height/2);
}
+
comp.render(canvas);
canvas.restore();
- canvas.save();
}
/// This implementation of update updates every component in the list.

diff:
@@ -8,12 +8,14 @@ class Camera with HasGameRef<RPGGame> {
double maxTop = 0;
double maxLeft = 0;
Position position = Position.empty();
+ double zoom;
- Rect get cameraRect => Rect.fromLTWH(
- position.x - 40,
- position.y - 40,
- gameRef.size.width + 80,
- gameRef.size.height + 80,
+ Camera({this.zoom = 1.0});
+
+ Rect get cameraRect => Rect.fromCenter(
+ center: Offset(position.x, position.y),
+ width: gameRef.size.width / zoom + 80,
+ height: gameRef.size.height / zoom + 80,
);
void moveTop(double displacement) {
@@ -66,12 +68,10 @@ class Camera with HasGameRef<RPGGame> {
Curve curve = Curves.decelerate,
}) {
if (gameRef?.size == null) return;
- final screenCenter =
- Position(gameRef.size.width / 2, gameRef.size.height / 2);
if (gameRef?.player != null) gameRef.player.focusCamera = false;
- double diffX = this.position.x - (position.x - screenCenter.x);
- double diffY = this.position.y - (position.y - screenCenter.y);
+ double diffX = this.position.x - (position.x);
+ double diffY = this.position.y - (position.y);
double originX = this.position.x;
double originY = this.position.y;
@@ -88,9 +88,8 @@ class Camera with HasGameRef<RPGGame> {
void moveToPosition(Position position) {
if (gameRef?.size == null) return;
if (gameRef?.player != null) gameRef.player.focusCamera = false;
- final screenCenter =
- Position(gameRef.size.width / 2, gameRef.size.height / 2);
- this.position = position - screenCenter;
+
+ this.position = position;
}
void moveToPlayerAnimated({Duration duration, VoidCallback finish}) {
@@ -112,8 +111,10 @@ class Camera with HasGameRef<RPGGame> {
Offset(gameRef.size.width / 2, gameRef.size.height / 2);
final positionPlayer =
worldPositionToScreen(gameRef.player.position.center);
+
final horizontalDistance = screenCenter.dx - positionPlayer.dx;
final verticalDistance = screenCenter.dy - positionPlayer.dy;
+
if (horizontalDistance.abs() > horizontal) {
this.gameRef.gameCamera.position.x += horizontalDistance > 0
? horizontal - horizontalDistance
@@ -135,11 +136,15 @@ class Camera with HasGameRef<RPGGame> {
Offset worldPositionToScreen(Offset position) {
return position.translate(
- -gameRef.gameCamera.position.x, -gameRef.gameCamera.position.y);
+ -gameRef.gameCamera.position.x + gameRef.size.width / 2,
+ -gameRef.gameCamera.position.y + gameRef.size.height / 2,
+ );
}
Offset cameraPositionToWorld(Offset position) {
return position.translate(
- gameRef.gameCamera.position.x, gameRef.gameCamera.position.y);
+ gameRef.gameCamera.position.x - gameRef.size.width / 2,
+ gameRef.gameCamera.position.y - gameRef.size.height / 2,
+ );
}
}

diff:
@@ -28,10 +28,11 @@ class Lighting extends GameComponent {
_visibleLight.forEach((light) {
light.update(_dtUpdate);
canvas.save();
- canvas.translate(
- -gameRef.gameCamera.position.x,
- -gameRef.gameCamera.position.y,
- );
+
+ canvas.translate(size.width/2, size.height/2);
+ canvas.scale(gameRef.gameCamera.zoom);
+ canvas.translate(-gameRef.gameCamera.position.x, -gameRef.gameCamera.position.y);
+
canvas.drawCircle(
Offset(light.gameComponent.position.center.dx,
light.gameComponent.position.center.dy),

original_message: allow camera zoom


message: create animateZoom and fix map not rendering on zoom chagne

diff:
@@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart';
class MapWorld extends MapGame {
double lastCameraX = -1;
double lastCameraY = -1;
+ double lastZoom = -1;
Size lastSizeScreen;
Iterable<Tile> _tilesToRender = List();
Iterable<Tile> _tilesCollisionsRendered = List();
@@ -25,9 +26,11 @@ class MapWorld extends MapGame {
@override
void update(double t) {
if (lastCameraX != gameRef.gameCamera.position.x ||
- gameRef.gameCamera.position.y != lastCameraY) {
+ lastCameraY != gameRef.gameCamera.position.y ||
+ lastZoom != gameRef.gameCamera.zoom) {
lastCameraX = gameRef.gameCamera.position.x;
lastCameraY = gameRef.gameCamera.position.y;
+ lastZoom = gameRef.gameCamera.zoom;
List<Tile> tilesRender = List();
List<Tile> tilesCollision = List();
@@ -71,6 +74,7 @@ class MapWorld extends MapGame {
lastCameraX = -1;
lastCameraY = -1;
+ lastZoom = -1;
mapSize = getMapSize();
gameRef.gameCamera.moveToPlayer(horizontal: 0, vertical: 0);
}
@@ -79,6 +83,7 @@ class MapWorld extends MapGame {
void updateTiles(Iterable<Tile> map) {
lastCameraX = -1;
lastCameraY = -1;
+ lastZoom = -1;
lastSizeScreen = null;
this.tiles = map;
verifyMaxTopAndLeft(gameRef.size);

diff:
@@ -128,6 +128,27 @@ class Camera with HasGameRef<RPGGame> {
gameRef.player.focusCamera = true;
}
+ void animateZoom({
+ double zoom,
+ Duration duration,
+ VoidCallback finish,
+ Curve curve = Curves.decelerate,
+ }) {
+ if (gameRef?.size == null) return;
+ if (zoom <= 0.0) return;
+
+ double diffZoom = this.zoom - (zoom);
+ double initialZoom = this.zoom;
+
+ gameRef.getValueGenerator(duration ?? Duration(seconds: 1))
+ ..addListenerValue((value) {
+ this.zoom = initialZoom - (diffZoom * value);
+ })
+ ..addListenerFinish(finish)
+ ..addCurve(curve)
+ ..start();
+ }
+
bool isComponentOnCamera(GameComponent c) {
if (gameRef?.size == null || c.position == null) return false;


message: add zoom to tiled map

diff:
@@ -56,6 +56,7 @@ class GameManualMap extends StatelessWidget implements GameListener {
background: BackgroundColorGame(Colors.blueGrey[900]),
gameController: _controller..setListener(this),
lightingColorGame: Colors.black.withOpacity(0.5),
+ zoom: 1.0, // you can change the game zoom here or directly on camera
);
});
}

diff:
@@ -65,6 +65,7 @@ class GameTiledMap extends StatelessWidget {
'chest', (x, y, width, height) => Chest(Position(x, y))),
background: BackgroundColorGame(Colors.blueGrey[900]),
lightingColorGame: Colors.black.withOpacity(0.6),
+ zoom: 1.0, // you can change the game zoom here or directly on camera
);
},
);

diff:
@@ -198,10 +198,10 @@ class Knight extends SimplePlayer with WithLighting {
if (showObserveEnemy) return;
showObserveEnemy = true;
showEmote();
-// if (!showTalk) {
-// showTalk = true;
-// _showTalk();
-// }
+ if (!showTalk) {
+ showTalk = true;
+ _showTalk();
+ }
},
);
}

diff:
@@ -57,6 +57,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.3"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.1"
collection:
dependency: transitive
description:
@@ -78,6 +85,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
flame:
dependency: transitive
description:
@@ -121,13 +135,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
- image:
- dependency: transitive
- description:
- name: image
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.12"
matcher:
dependency: transitive
description:
@@ -155,7 +162,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
- version: "1.6.4"
+ version: "1.7.0"
path_drawing:
dependency: transitive
description:
@@ -212,13 +219,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
- quiver:
- dependency: transitive
- description:
- name: quiver
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.1.3"
sky_engine:
dependency: transitive
description: flutter
@@ -272,7 +272,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
- version: "0.2.15"
+ version: "0.2.16"
tiled:
dependency: transitive
description:

diff:
@@ -22,9 +22,10 @@ class BonfireTiledWidget extends StatefulWidget {
final Color lightingColorGame;
final TiledWorldMap tiledMap;
final Widget progress;
+ final double zoom;
- const BonfireTiledWidget(
- {Key key,
+ const BonfireTiledWidget({
+ Key key,
this.joystick,
this.player,
this.interface,
@@ -37,8 +38,9 @@ class BonfireTiledWidget extends StatefulWidget {
this.collisionAreaColor,
this.lightingColorGame,
this.tiledMap,
- this.progress})
- : super(key: key);
+ this.progress,
+ this.zoom,
+ }) : super(key: key);
@override
_BonfireTiledWidgetState createState() => _BonfireTiledWidgetState();
}
@@ -108,6 +110,7 @@ class _BonfireTiledWidgetState extends State<BonfireTiledWidget>
collisionAreaColor:
widget.collisionAreaColor ?? Colors.lightGreenAccent.withOpacity(0.5),
lightingColorGame: widget.lightingColorGame,
+ zoom: widget.zoom,
);
setState(() {
_loading = false;

original_message: add zoom to tiled map